Navigating in the dynamics 365 CRM App - javascript

I have a Dynamics CRM 2016 Online installation. I have created a web resource in this application. This web resource is launched from a button in the account list.
On the desktop this works perfectly, it launches a new window and I can do what I need and then navigate to a new record in crm with
Xrm.Utility.openEntityForm(entity, entityId);
and then close the window with
window.close();
I can also access this webbresource in the (iOS) phone app. But neither of the above two commands work when the resource has loaded.
I can't navigate from the webresource to a record using any of these commands:
Xrm.Utility.openEntityForm(entity, entityId); // throws undefined error
window.open(recordURL); // does nothing
window.location.href = recordURL; // does nothing
And I cannot close the webresource with either of these:
window.close(); // does nothing
window.history.go(-1); // goes to a blank page (even more infuriating).
Are there some specific javascript commands for these things (navigate to record, or go back)? I can't seem to find any references to what commands I should be using in the dynamics 365 app.

This Microsoft documentation site has some information about mobile devices:
"Additionally, Dynamics 365 for phones does not support web
resources and IFRAMES so the client APIs for these controls won't
work. Client APIs for web resources and IFRAMES are supported on
Dynamics 365 for tablets though."
"Dynamics 365 mobile clients also do not support the window.open
method. If you are looking to open an entity form for a new or
existing record, use Xrm.Utility.openEntityForm instead."
So the window functions are not supported. Xrm.Utility.openEntityForm() should work though. Maybe a web API request doesn't deliver a required guid for the function call due to a bad internet connection on mobile devices.
Maybe it's just not supported as well, but nowhere documented. Atleast a very similar function Xrm.Utility.openWebResource() is not supported as state here: Xrm.Utility.openWebresource()
Keep in mind that you have to use parent.Xrm to access the Xrm object inside of webpages that are embedded as an iframe inside a entity form. That counts for webpages opened by window.open() too.
In case it will just not work you could still do some kind of conditional behavior or fallback:
var isCrmForMobile = (Xrm.Page.context.client.getClient() == "Mobile")
if (isCrmForMobile)
{
// Code for CRM for phones and tablets only goes here.
}
else
{
// Code for web browser or CRM for Outlook only goes here.
}
if (Xrm.Page.context.client.getClient() == "Mobile" && Xrm.Page.context.client.getFormFactor() == 3)
{
// Add code that should only run in CRM for phones here
}
// Code for any client goes here.

Related

ios PWA How to open external link on mobile default safari(not In app browser)

I'm making a PWA in IOS 13.2.
I need to launch another PWA from my app by clicking an external link.
The problem is that my current PWA opens all external links in the in-app browser instead of regular safari and there is no add to Home screen option in the in-app browser.
How can I force the PWA to open an external link in regular safari instead of in-app safari?
Things that doesn't work
Changing the scope in the manifest-file
Using rel="noreferrer"
Using target="_blank"
It's a bit hard to answer this question (and all the comments) as the use case isn't overly clear, but here goes...
On mobile devices, an "in-app browser" is NOT the same thing as a Progressive Web App running in full-screen mode.
If an iOS app runs and then displays HTML content inside of it, it's utilizing UIWebView or WKWebView. However, in the case of a PWA it's already running in Safari as a "full screen" experience. Defining which you're trying to break links out of is extremely important as they function differently.
target="_blank" will typically break a link out of a page using WebView. I believe this is the default functionality for links outside the current domain as well.
An "installed" PWA is running in something called "Stand Alone" mode. This makes it full screen and removes navbars, etc. As of this writing, Safari doesn't support the fullscreen API that other browsers are implementing. Chrome uses the App manifest file to determine this functionality. Safari basically ignores the manifest in favor of proprietary meta tags.
In this case <meta name="apple-mobile-web-app-capable" content="yes"> tells Apple to make the page a stand-alone app. Try setting content="no" (Safari caches things heavily so you might need to force a refresh) on the pages that should break out of stand-alone mode. You can check to see what mode the page thinks it's in by using this javascript boolean window.navigator.standalone.
Or you can use javascript to force a "new window" in Safari as long as you're targeting a different subdomain or HTTP instead of HTTPS.
// if app is hosted from https://example.com
if (("standalone" in window.navigator) || window.navigator.standalone ) {
window.open('http://example.com/page', '_blank');
}
Finally, Apple uses some special URL strings to cause native apps to handle some actions like emails, phone numbers, and youtube videos. You might be able to "hack" that functionality to get safari to open your link.
After quite thorough investigations i believe this isn't possible currently (between iOS 13.X and iOS 14.1).
The following javascript API's will use in-app browser:
window.open()
window.location.href =
Using an anchor tag will also use the in-app browser no matter what attributes it is assigned. Changing the scope in the manifest also doesn't help.
BUT i did find a way to at least prompt the user that they are in an in-app browser to make the UX a little less confusing.
There are two ways (maybe more?) to detect if the browser is standalone: window.matchMedia("(display-mode: standalone)").matches and window.navigator.standalone . And here is the weird part:
// regular safari
window.navigator.standalone -> false
window.matchMedia("(display-mode: standalone)").matches -> false
// full screen apps (only pwas, weirdly enough this doesn't apply to pre-pwa web apps on iOS)
window.navigator.standalone -> true
window.matchMedia("(display-mode: standalone)").matches -> true
// in-app browsers launched from within a PWA
window.navigator.standalone -> true
window.matchMedia("(display-mode: standalone)").matches -> false
I assume window.navigator.standalone represents the parent context of the pwa and window.matchMedia("(display-mode: standalone)").matches represents the context of the in-app browser.
So a naive implemention to check if your app is running in the in-app browser on iOS:
function isIOSInAppBrowser() {
// Check if device is iPad, iPhone or iPod (this bit is naive and should probably check additional stuff)
if (Boolean(window.navigator.userAgent.match(/iPad|iPhone|iPod/)) === false) return false;
// Check if navigator is standalone but display-mode isn't
if (window.navigator.standalone === true && window.matchMedia("(display-mode: standalone)").matches === false) {
return true;
} else {
return false;
}
}
note that this implementation isn't reliable and can easily produce false positive in future version of iOS
Tested with latest iOS-12/iPhone 7
If anyone could test with different device and iOS versions.
iOS is opening <a> tag as external url, launching Safari.
So, create a hidden tag:
HTML
<a hidden id='openSafari' href=''>click me</a>
script
let openSafari = document.getElementById('openSafari');
openSafari.setAttribute('href', 'https://netflix.com');
openSafari.click()
The way your app will work based on what did you configured in manifest.json file.
In manifest file the scope key will determines which link will open inside PWA and which link will open in browser.
For example route under myapp will open inside PWA and other would be outside.
{
"name": "My App",
"start_url": "/myapp",
"scope": "/myapp",
"display": "standalone"
}
I would personally try the target="_blank" to the href link. I would also add a rel="noreferrer external" to ensure this is consistent across different webview implementations. There should also be a way to do this via the manifest file although I'm not entirely sure how that works in iOS.
#IBAction func openURL(_ sender: Any) {
// check if website exists
guard let url = URL(string: "https://apple.com") else {
return
}
let safariVC = SFSafariViewController(url: url)
present(safariVC, animated: true, completion: nil)
}
This will work perfectly on all iOS versions with PWA support. (I know this because I am a longtime iOS user):
Add target="_blank" to all the links that you have. That will open them in the mobile default safari.
For example:
Page

Dynamics CRM Online: Xrm.Page.context.client.getClient() returns "Web" when called from Outlook App

The Xrm.Page.context.client.getClient() function does not seem to be working correctly. When called from the desktop Outlook App, it returns "Web", instead of "Outlook" as described by the documentation (https://msdn.microsoft.com/en-us/library/gg334511.aspx#BKMK_getclient). Here is the code I am using (bound to form onLoad event):
function alertClient() {
var client = Xrm.Page.context.client.getClient();
Xrm.Utility.alertDialog(client);
}
Any ideas on why this is happening?
Can you mention which app you use? There is two: The "Dynamics 365 for Outlook" or the "Dynamics 365 app for Outlook".
I'm not sure if nowaday this function still returns "Outlook". Now, everything runs in an iframe in Outlook. Before the add-on had a different architecture.
As a solution, maybe if you parse the url from Outlook you could find a specific parameter that would tell you the source. That's probably how Dynamics would do it anyway.

Facebook app browser debugging [duplicate]

I'm developing website with a lot of HTML5 and CSS3 features. I'm also using iframe to embed several content on my website. It works fine if I open it using Chrome/Firefox/Safari mobile browser. However, if I share on facebook (post/page) and I opened it up with Facebook application with Facebook Internal Browser, my website is messed up.
Is there any tools or way to debug on Facebook Browser? Thanks.
This is how you can do the debugging yourself. It's painful, but the only way I've come across so far.
tl;dr Get the Facebook App loading a page on your local server so you can iterate quickly. Then print debug statements directly to the page until you figure out what is going on.
Get a link to a page on your local server that you can access on your mobile device (test in mobile safari that it works). See this to find out your local IP address How do you access a website running on localhost from iPhone browser. It will look something like this
http://192.xxx.1.127:3000/facebook-test
Post that link on your Facebook page (you can make it private so your friends aren't all like WTF?)
Click the posted link in the Facebook mobile App and it will open up in Facebook's mobile browser
Since you don't have a console, you basically need to print debug statements directly to the page so it is visible. Put debug statements all over your code. If your problems are primarily related to CSS, then you can iteratively comment out stuff until you've found the issue(s) or print the relevant CSS attributes using JavaScript. Eg something like (using JQuery)
function debug(str){$('body').append("<br>"+str);}
Quite possibly the most painful part. The Facebook browser caches very aggressively. If you are making changes and nothing has happened, it's because the content is cached. You can sometimes resolve this by updating the URLs, eg /facebook-test-1, /facebook-test-2, or adding dummy parameters eg /facebook-test?dummy=1. But if the changes are in external css or js sheets it sometimes will still cache. To 100% clear the cache, delete the Facebook App from your mobile device and reinstall.
The internal browser the Facebook app uses is essentially a uiWebView. Paul Irish has made a simple iOS app that lets you load any URL into a uiWebView which you then can debug using Safari's Developer Tools.
https://github.com/paulirish/iOS-WebView-App
I found a way how to debug it easier. You will need to install the Ghostlab app (You have a 7-day free trial there, however it's totally worth paying for).
In Ghostlab, add the website address (or a localhost address) you want to debug and start the session.
Ghostlab will generate a link for access.
Copy that link and post it on Facebook (as a private post)
Open the link on mobile and that's it! Ghostlab will identify you once you open that link, and will allow you to debug the page.
For debugging, you will have all the same tools as in the Chrome devtools (how cool is that!). For example, you can tweak CSS and see the changes applied live.
If you want to debug a possible error, you can try to catch it and display it.
Put this at the very top of your code:
window.onerror = function (msg, url, lineNo, columnNo, error) {
var string = msg.toLowerCase();
var substring = "script error";
if (string.indexOf(substring) > -1){
alert('Script Error: See Browser Console for Detail');
} else {
var message = [
'Message: ' + msg,
'URL: ' + url,
'Line: ' + lineNo,
'Column: ' + columnNo,
'Error object: ' + JSON.stringify(error)
].join(' - ');
alert(message);
}
}
(Source: MDN)
This will catch and alert your errors.
Share a link on Facebook (privately), or send yourself a message on Facebook Messenger (easier). To break the cache, create a new URL every time, e.g. by appending a random string to the URL.
Follow the link and see if you can find any errors.
With help of ngrok create temporary http & https adress instead of your ordinary localhost:3000(or other port) and you could run your app on any devices. It is super easy to use.
and as it was written above all other useful information you should write somewhere inside div element (in case of React I recommend to put onClick on that div with force update or other function for getting info, sometimes it helps because JS in FB could be executed erlier than your information appears). Keep in mind that alerts are not reliable, sometimes they are blocked
bonus from ngrok that in console you will see which files was
requested and response code (it will replace lack of network tab)
and about iFrame.If you use it on other domain and you rely on cookies - you should know that facebook in-app browser blocks 3rd party cookies
test on Android and iOS separately because technicaly they use different browsers

Chrome extensions for silent print?

I have made a silent print web application that prints a PDF file. The key was to add JavaScript to the PDF file that silently print itself.
To do this I open the PDF with acrobat reader in chrome, that allow me to execute the script (with the proper permissions).
But as it was announced this solution won't work after chrome 45 because the npapi issue.
I guess a possible solution could be to use the recently release printProvider of chrome extensions.
Nevertheless I can't imagine how to fire any of the printProvider events.
So the question is: Is ok to think in chrome extensions to make a silent print web application, and how can I fire and handle a print job for an embedded PDF of a HTML Page.
Finally I reached an acceptable solution for this problem, as I couldn't find it out there, but read to many post with the same issue I will leave my solution here.
So first you need to add your printer to the Google Cloud Print and then you will need to add a proyect to the Google Developers Console
Then add this script and any time you need to print something execute the print() function. This method will print the document indicated in the content
The application will ask for your permission once to manage your printers.
function auth() {
gapi.auth.authorize({
'client_id': 'YOUR_GOOGLE_API_CLIENT_ID',
'scope': 'https://www.googleapis.com/auth/cloudprint',
'immediate': true
});
}
function print() {
var xhr = new XMLHttpRequest();
var q = new FormData()
q.append('xsrf', gapi.auth.getToken().access_token);
q.append('printerid', 'YOUR_GOOGLE_CLOUD_PRINTER_ID');
q.append('jobid', '');
q.append('title', 'silentPrintTest');
q.append('contentType', 'url');
q.append('content',"http://www.pdf995.com/samples/pdf.pdf");
q.append('ticket', '{ "version": "1.0", "print": {}}');
xhr.open('POST', 'https://www.google.com/cloudprint/submit');
xhr.setRequestHeader('Authorization', 'Bearer ' + gapi.auth.getToken().access_token);
xhr.onload = function () {
try {
var r = JSON.parse(xhr.responseText);
console.log(r.message)
} catch (e) {
console.log(xhr.responseText)
}
}
xhr.send(q)
}
window.addEventListener('load', auth);
<script src="https://apis.google.com/js/client.js"></script>
Anyway this script throw a 'Access-Control-Allow-Origin' error, even though this appears in the documentation... I couldn't make it work :(
Google APIs support requests and responses using Cross-origin Resource Sharing (CORS). You do not need to load the complete JavaScript client library to use CORS. If you want your application to access a user's personal information, however, it must still work with Google's OAuth 2.0 mechanism. To make this possible, Google provides the standalone auth client — a subset of the JavaScript client.
So to go throw this I had to install this chrome extension CORS. I'm sure that some one will improve this script to avoid this chrome extension.
You can register an Application to a URI Scheme to trigger the local application to print silently. The setting is pretty easy and straightforward. It's a seamless experience. I have posted the solution here with full example:
https://stackoverflow.com/a/37601807/409319
After the removal of npapi, I don't believe this is possible solely programmatically. The only current way I know to get chrome to print silently is using chrome kiosk mode, which is a flag (mode) you have to set when starting chrome.
Take a look at these SO posts:
Silent printing (direct) using KIOSK mode in Google Chrome
Running Chrome with extension in kiosk mode
This used to be possible using browser plugins (e.g. Java + NPAPI, ActiveX) but has been blacklisted by most browsers for several years.
If interested in modern solutions that use similar techniques, the architecture usually requires the following:
WebSocket, HTTP or Custom URI connection back to localhost
API that talks through web transport (JavaScript or custom URI scheme) to an app running locally.
A detail of projects (several of them are open source) that leverage these technologies are available here:
https://stackoverflow.com/a/28783269/3196753
Since the source code of these projects can vary (hundreds of lines to tens-of-thousands of lines), a code snippet would be too large unless a inquiring about a specific project's API.
Side note: Some technologies offer dedicated cloud resources, which add convenience at the expense of potential latency and privacy. At the time of writing this, the most popular "free" cloud solution -- Google Cloud Print -- is slated to be retired in December 2020.

Office Name Control PresenceEnabled is always false

I have a WPF app with a WebBrowser control, which loads an HTML file from the local disk. In the HTML file, I have javascript code to create an Office Lync Presence ActiveX Control (Name.NameCtrl.1) object which is used to display the contact card for some users.
The object gets created but the PresenceEnabled property for the Name Control is always false. Any ideas how I can work around this problem ?
I have Lync 2013 installed on the computer. The same code worked fine when I used Lync 2010.
My browser is IE 9 and I have observed the same issue on IE 8 and IE 10 as well (currently targetting only IE).
Javascript Code:
try {
var presenceObj = new window.ActiveXObject("Name.NameCtrl.1");
} catch (err) { }
function showLync(element) {
try {
// Works fine till this part. However, presenceObj.PresenceEnabled is false.
presenceObj.ShowOOUI("somecalculatedalias", 0, $(element).offset().left, $(element).offset().top);
} catch (err) {
// goes into the catch block above with a "Permission denied" error (-2146828218)
}
}
More details:
I have added the MOTW (mark of the web) to my HTML page to make sure it shows up without the warning and I can see that the page does load in the correct zone. From page properties: Local intranet | Protected Mode: Off.
When I remove this MOTW, I get a the warning as expected saying "To Help protect your security, your web browser has restricted this file... (blah blah)". And when I allow the blocked content, the same code above works fine and I can see the Lync flyout from the ShowOOUI call.
Any ideas how I can work around this problem ?
Place the AllowPartiallyTrustedCallers (APTCA) attribute on the assembly.
Here are some other alternatives:
Security Zones
Mark of the Web
The PresenceEnabled property is false if the control is used on a page that is not on the intranet or on a trusted site, or if a supported version of an instant messaging program such as Windows Live Messenger/Skype for Business is not running.

Categories

Resources