I need to create a JS-Library which can run workflow using new WebApi for Dynamics CRM 2016:
https://msdn.microsoft.com/de-de/library/mt607689.aspx
I need to start workflow from my Code. (workflow should be “real-time”) and not asynchronously . I will build my function-call into Ribbon on form.
If anyone can help me I would be more then thankful, since I searched all internet and could not found how to solve this, except from above link where I found this method
https://msdn.microsoft.com/de-de/library/mt622404.aspx
but I'm not sure how to use this method? Once agin it has to be “real-time”
I found solutions such as:
-https: //processjs.codeplex.com/
but this does not work for me since it run workflow asynchronously. It has to be using Web API from link provided above. I think that this Web API works only for Microsoft Dynamics 2016
Now that we have actions there really isn't a need to start a workflow from javascript anymore. I used to do so using a javascript library that used the SOAP api but the web api actions are much easier to use. And an action is created in the same way as a workflow. To create an action go to create a workflow but instead of choosing workflow from the dropdown select action. You will end up with a form like this.
Remember the unique name and the entity which you will run it against. In this example I'll be using this workflow pictured which runs against a contact record.
From javascript I can now issue a POST to
https://<your-crm-server>/api/data/v8.0/contacts(<contact-id>)/Microsoft.Dynamics.CRM.wa_GetContactSyncStatus
Again this is an action targeting contacts and running the wa_GetContactSyncStatus action, change the values to what you need them to be. Also as a side note this is against a 2016 server anything later will have a different api version for you to use. Consult the developer resources page in your crm instance to figure out what your url for the web api is.
The action will run asynchronously and as long as your javascript request is set to be synchronous as well your request will return when the action is complete.
As another side note if you have your action call another workflow that isn't synchronous it will quite probably return before your asynchronous background workflow does.
I do this quite often: make the process an Action, they are designed specifically for this purpose (click a ribbon button and invoke what essentially is a workflow through WebAP; they also become custom messages for plugin registration, which is nice in some scenarios).
To have synchronous invocations all you need to do is to make the XmlHttpRequest synchronous by tweaking the open statement:
// 'xhr' is the XMLHttpRequest
xhr.open(http_method, request_url, false); <-- third parameter 'false' means sync request
I never use libraries to invoke the webapi so unfortunately I can't suggest any library-specific piece of code, but I would assume any decent library allows you to make XHR requests synchronous.
(Mandatory warning: sync requests are suboptimal and browsers do complain about them, I expect Chrome in particular to start breaking sync code at some point in the future).
Soap Request in JS :
function RunWorkflow(in_entitiId,in_workflowId,in_url) {
var _return = window.confirm('Do you want to execute workflow ?');
if (_return) {
var url = in_url;
var entityId =in_entitiId ;
var workflowId = in_workflowId;
var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
url = url + OrgServicePath;
var request;
request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
"<s:Body>" +
"<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<request i:type=\"b:ExecuteWorkflowRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">" +
"<a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">" +
"<a:KeyValuePairOfstringanyType>" +
"<c:key>EntityId</c:key>" +
"<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">" + entityId + "</c:value>" +
"</a:KeyValuePairOfstringanyType>" +
"<a:KeyValuePairOfstringanyType>" +
"<c:key>WorkflowId</c:key>" +
"<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">" + workflowId + "</c:value>" +
"</a:KeyValuePairOfstringanyType>" +
"</a:Parameters>" +
"<a:RequestId i:nil=\"true\" />" +
"<a:RequestName>ExecuteWorkflow</a:RequestName>" +
"</request>" +
"</Execute>" +
"</s:Body>" +
"</s:Envelope>";
var req = new XMLHttpRequest();
req.open("POST", url, true)
// Responses will return XML. It isn't possible to return JSON.
req.setRequestHeader("Accept", "application/xml, text/xml, */*");
req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
req.onerror = displayError;
req.onreadystatechange = function () { assignResponse(req); };
req.send(request);
}
function displayError(e) {
alert(this.status);
}
}
function assignResponse(req) {
if (req.readyState == 4) {
if (req.status == 200) {
alert('successfully executed the workflow');
}
}
}
Example:
RunWorkflow(Xrm.Page.data.entity.getId(),"21E95262-5A36-46CA-B5B5-3F5AA539A9AF","https://org.dynamics.com");
Related
** JAVASCRIPT question **
I'm using regularly ajax via XMLHttpRequest. But in 1 case, I need 1 ajax call per seconds....
but long term wise and with growing number of simultaneous users, it could bloat easily...
I'm reading stuff about webRTC data channel and it seems interesting and promissing.
Here my working AJAX function as an example of how easy and there is a few lines of codes to communicate from the browser to the server and vice-versa
function xhrAJAX ( divID , param2 ) {
// random value for each call to avoid cache
var pcache = (Math.floor(Math.random() * 100000000) + 1);
// parameters
var params = "divID="+encodeURIComponent(divID)+"¶m2="+encodeURIComponent(param2);
// setup XMLHttpRequest with pcache
var xhr = new XMLHttpRequest();
xhr.open("POST", "/file.php?pcache="+pcache, true);
// setup headers
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// prepare onready scripts
xhr.onreadystatechange = function(e) { if (xhr.readyState == 4) { $("#"+divID).html(e.currentTarget.responseText) ; } }
// send the ajax call
xhr.send(params);
}
How can I "transpose" or "convert" this ajax workflow into a webRTC data channel ? in order to avoid to setup a setInterval 1000...
Note: I mean how to replace the javascript portion of the code. PHP here is only to illustrate, I don't want to do a webRTC via PHP...
Is there a simple few lines of code way to push/receive data like this ajax function ?
the answer I'm looking for is more like a simple function to push and receive
(once the connection with STUN, ICE, TURN is established and working...)
If I need to include a javascript library like jquery or the equivalent for webRTC, I'm welcoming good and simple solution.
*** The main goal is this kind of scenario :
I have a webapp : users in desktop and users within webview in Android and IOS
right now I have this workflow => ajax every 3 seconds to "tell" the main database that the user is still active and using the browser (or the app)
But I'd like to replace with this kind : when the user uses the browser => do a webrtc data chata in background between the browser and the server
While reading on the web I think that webRTC is a better solution than websocket.
** I did a bit of search and found peerjs....
https://github.com/jmcker/Peer-to-Peer-Cue-System/blob/main/send.html
I'll do some testing, but in the meantime, if someone can trow ideas, it could be fun.
Cheers
How can I run JavaScript function from appropriate web resource with a plugin in CRM2011.
I couldn't find any information on the Internet. Most of the resources describes how to trigger plugin from JS, but not opposite.
Here's JS code taht is copying the notes to the description field.
When save is clicked. you can see that the data is correctly display in a description field. However if you press save and close and open form again the description field will be empty.
I thought that the reason for that is that the JS executed after safe event but later tests descovered that it's false. Could someone point out an error in this JS code which cause that data is not saving?
Or give a suggestion how's write a plugin which is retrieving data from related entity and writes it into field in the form. thanx
function copyNotes()
{
// CLEAR DESCRIPTION FIELD
alert("JS");
Xrm.Page.getAttribute("description").setValue('');
// GET ID OF THE CASE AND CLEAN IT AND GET URL for oData stuff
//THEN CALL RETRIEVE FUNCTION
var caseID = Xrm.Page.data.entity.getId();
caseID = caseID.replace('{', '').replace('}', '');
var oDataPath = Xrm.Page.context.getServerUrl() + "/xrmservices/2011/organizationdata.svc";
ODataPath = Xrm.Page.context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc";
retrieveRecord(caseID);
}
// CREATE AN HTTP REQUEST AND SEND IT
function retrieveRecord(Id) {
var retrieveReq = new XMLHttpRequest();
retrieveReq.open("GET", ODataPath + "/AnnotationSet?$filter=ObjectId/Id" + " eq (guid'" + Id + "')", true);
retrieveReq.setRequestHeader("Accept", "application/json");
retrieveReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
retrieveReq.onreadystatechange = function() {
// THIS HANDLES A CALLBACK
retrieveReqCallBack(this);
};
retrieveReq.send();
}
function retrieveReqCallBack(retrieveReq) {
if (retrieveReq.readyState == 4 /* complete */ )
{
if (retrieveReq.status == 200) {
//Success
var retrieved = this.parent.JSON.parse(retrieveReq.responseText).d;
// ITERATE THROUGH THE NOTES FOR THIS CASE
for (var i = 0; i < retrieved.results.length; i++) {
// IF IS AN EMPTY FIELD ADD 'case details:'
if (Xrm.Page.getAttribute("description").getValue() == null || Xrm.Page.getAttribute("description").getValue() == 'null') {
Xrm.Page.getAttribute("description").setValue('Case details:');
}
// BOF PARSE DATE
var date = retrieved.results[i].CreatedOn;
date = new Date(parseInt(date.replace("/Date(", "").replace(")/", ""), 10));
// EOF PARSE DATE
var newtext = "--------------------\r\n" + "Created by: " + retrieved.results[i].CreatedBy.Name + " - " + date + "\r\n" + retrieved.results[i].NoteText + "\r\n--------------------\r\n";
var text = Xrm.Page.getAttribute("description").getValue() + "\r\n" + newtext;
Xrm.Page.getAttribute("description").setValue(text);
}
}
}
}
There is no supported way to call back to the client from the server from within the plugin. I'm also not aware of any unsupported way.
I don't think this question even makes sense. Plugin's only trigger when there has been a CRUD operation of some sort. Any CRUD operation triggered by the GUI will result in a refresh of the entity any way. You could perform an update via javascript and an Odata call, but then once the plugin has finished, you can run whatever javascript you'd like to run.
There's no (reasonable) way to do that.
The reason for that is the fact that plugin is a server-size executed thingy. It can't even assume there's a GUI. (Of course, we know there is but generally, a server-size code can't interact with the GUI directly).
JavaScript is client-side code and a client assumes a server. That's (roughly) why JS can call a plugin (although I wouldn't put it that way) but not the other way around.
I've never had a need of such an operation so I'm curious as to what your task is. Are you asking of pure, academic interest or is it a part of a design? Perhaps there's a better way to reach your goal?
I've been working on a Windows gadget (meaning the "browser" is Internet Explorer) that queries specified subnet addresses for information. Now, it sometimes does this at a relatively quick pace (roughly every 5 seconds) and it works well enough. However, sometimes it will get stuck at ready state 1 and will just stay there forever. Whenever the gadget tries to redo the function for getting the xmlhttprequest and getting information from it it will stay at state 1. This is easily replicable when opening multiple instances of the gadget and then closing all but one of them. At that point, the one that's still open will almost always get stuck in this state. I feel like it might have something to do with them all accessing the same website, or it may just have to do with xmlhttprequests being stopped mid-transmission and that preventing another from working. Below is the relevant code.
//Reference to this for the inner function
var me = this;
var request = new XMLHttpRequest();
request.onreadystatechange = onReadyStateChange;
var url = this.url;
//Make the URL random to prevent being cached
url += ("&a=" + ((new Date()).getTime()));
Trace(DEBUG_COMM, "Sase.updateStatus url: " + url);
request.open("GET", url, true);
request.send(); // fire off the request, calls httpRequestReadyStateChange as things continue
Trace(DEBUG_COMM, "Request sent" + request.readyState);
function onReadyStateChange() {Trace(DEBUG_COMM, "Sase.httpRequestReadyStateChange: state=" + request.readyState);
if (4 == request.readyState) {
Trace(DEBUG_COMM, "Sase.httpRequestReadyStateChange: status=" + request.status);
if (request.status == 200) {
Trace(DEBUG_COMM, "retrieved html: " + request.responseText);
var results = request.responseText;
var resultsString = request.responseText.toString();
Trace(DEBUG_COMM, "results String: " + resultsString);
me.ParseStatusData(resultsString);
}
else {
//me.commError(request.status);
}
request = null;
}
}
Well it looks like I figured it out. I had a feeling it was an unresolved request, since it only happens when instances of it are closed (meaning that if one of them is closed while in communication with the server it stays in communication forever and no one else can access the server) and it appears that is the case. I made some modifications to the code in multiple areas and basically what it comes down to is when the gadget closes it makes sure to abort all of the requests. The requests are now instance variables (to allow for the aborting of them), but are still made new everytime they are needed.
For those who stumble across this and need a concrete code example, here you go.
I had the same problem and the solution was to re-use the XMLHttpRequest object, to ensure that any previous request was cancelled before initiating a new one. This won't work if you want to have multiple AJAX requests flying around but in my case triggering a new request meant that the last one was no longer needed.
All of the requests on my page went through the same XMLHttpRequest wrapper method which looked like this;
//Declare the XMLHttpRequest object to be re-used
var global_xHttpRequest = null;
function xmlHttpHandler(type, params, complete, postData) {
//Prevents an issue where previous requests get stuck and new ones then fail
if (global_xHttpRequest == null) {
global_xHttpRequest = new XMLHttpRequest();
} else {
global_xHttpRequest.abort();
}
//Parse out current URL
var baseURL = window.location.host;
var svc = "https://" + baseURL + "/processAction?";
var url = svc + params;
global_xHttpRequest.open(type, url, true);
//Add the callback
global_xHttpRequest.onreadystatechange = complete;
global_xHttpRequest.send(postData);
}
Which can be used like this:
xmlHttpHandler("GET", params, completeFnc);
I have an issue, mainly with IE.
I need to be able to handle n queries one after another. But If I simply call my function below in a for loop IE does some strange things (like loading only so many of the calls).
If I use an alert box it proves that the function gets all of the calls, and surprisingly IT WORKS!
My guess is that IE needs more time than other browsers, and the alert box does just that.
Here is my code:
var Ajax = function(all) {
this.xhr = new XMLHTTPREQUEST(); // Function returns xhr object/ activeX
this.uri = function(queries) { // Takes an object and formats query string
var qs = "", i = 0, len = size(queries);
for (value in queries) {
qs += value + "=" + queries[value];
if (++i <= len) { qs += "&"; }
}
return qs;
};
xhr.onreadystatechange = function() { // called when content is ready
if (this.readyState === 4) {
if (this.status === 200) {
all.success(this.responseText, all.params);
}
this.abort();
}
};
this.post = function() { // POST
xhr.open("POST", all.where, true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send(uri(all.queries));
};
this.get = function() { // GET
xhr.open("GET", all.where + "?" + uri(all.queries), true);
xhr.send();
};
if (this instanceof Ajax) {
return this.Ajax;
} else {
return new Ajax(all);
}
};
This function works perfectly for a single request, but how can I get it to work when called so many times within a loop?
I think the problem might be related to the 2 concurrent connections limit that most web browsers implement.
It looks like the latency of your web service to respond is making your AJAX requests overlap, which in turn is exceeding the 2 concurrent connections limit.
You may want to check out these articles regarding this limitation:
The Dreaded 2 Connection Limit
The Two HTTP Connection Limit Issue
Circumventing browser connection limits for fun and profit
This limit is also suggested in the HTTP spec: section 8.14 last paragraph, which is probably the main reason why most browsers impose it.
To work around this problem, you may want to consider the option of relaunching your AJAX request ONLY after a successful response from the previous AJAX call. This will prevent the overlap from happening. Consider the following example:
function autoUpdate () {
var ajaxConnection = new Ext.data.Connection();
ajaxConnection.request({
method: 'GET',
url: '/web-service/',
success: function (response) {
// Add your logic here for a successful AJAX response.
// ...
// ...
// Relaunch the autoUpdate() function in 100ms. (Could be less or more)
setTimeout(autoUpdate, 100);
}
}
}
This example uses ExtJS, but you could very easily use just XMLHttpRequest.
Given that the limit to a single domain is 2 concurrent connections in most browsers, it doesn't confer any speed advantage launching more than 2 concurrent requests. Launch 2 requests, and dequeue and launch another each time one completes.
I'd suggest throttling your requests so you only have a few (4?) outstanding at any given time. You're probably seeing the result of multiple requests being queued and timing out before your code can handle them all. Just a gess though. We have an ajax library that has built-in throttling and queues the requests so we only have 4 outstanding at any one time and don't see any problems. We routinely q lots per page.
Your code looks like it's put together using the constructor pattern. Are you invoking it with the new operator like var foo = new Ajax(...) in your calling code? Or are you just calling it directly like var foo = Ajax(...) ?
If the latter, you're likely overwriting state on your later calls. It looks like it's designed to be called to create an object, on which the get/post methods are called. This could be your problem if you're "calling it within a loop" as you say.
Is there a way to consume a web service using JavaScript? I'm Looking for a built-in way to do it, using a JavaScript framework is not an option.
You can consume a web service using JavaScript natively using the XmlHttpRequest object. However instantiating this object varies between browsers. For example Firefox and IE 7+ let you instantiate it as a native JavaScript object but IE6 requires you to instantiate it as an ActiveX control.
Because of this I'd recommend using an abstraction library such as jQuery. If that's not an option then abstract the creation into a factory method and check for the browser version.
To use this to make a web service call you simply instantiate the object and then call it's open() method. I recommend this is done async to keep the UI responsive. When invoked async you will get callbacks to your specified async method that will indicate the status of the request. When the status is 4 (loaded) you can take the response data and then process it.
How you process the data will depend on what it is, if it's JSON then you can run it through JavaScript's eval() method but that does have some security implications. If it's XML you can use the XML DOM to process it.
See Wikipedia for more info on the XMLHttpRequest object.
You can create an XMLHttpRequest if the service is hosted within your domain. If not, you will have cross-domain issues.
You can use the XMLHttpRequest object, but since you don't want to use any JavaScript frameworks, you will have to marshal and unmarshal the SOAP envelopes yourself.
Also check XML HTTP Request for a nice info page about using the XmlHttpRequest object.
There is a small library written in javascript that can be used as a XML-SOAP client.
I don't know if it works on all browsers but it might help you out. You can find it here
This worked. It's old (checking for Netscape), was written before all the Ajax tools came out. You do have to handle different browsers -- basically, IE does it one way, and everybody else does it the other way.
// javascript global variables
var soapHeader = '<?xml version=\"1.0\"?>'
+ '<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"'
+ ' SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"'
+ ' xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\"'
+ ' xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\"'
+ '>'
+ '<SOAP-ENV:Header/>'
+ '<SOAP-ENV:Body>';
var soapFooter = '</SOAP-ENV:Body>'
+ '</SOAP-ENV:Envelope>';
var destinationURI = '/webservices/websalm';
var actionURI = '';
function callWebService(nsCallback,ieCallback,parms) {
try
{
// Create XmlHttpRequest obj for current browser = Netscape or IE
if (navigator.userAgent.indexOf('Netscape') != -1)
{
SOAPObject = new XMLHttpRequest();
SOAPObject.onload = nsCallback;
} else { //IE
SOAPObject = new ActiveXObject('Microsoft.XMLHTTP');
SOAPObject.onreadystatechange = ieCallback;
}
SOAPObject.open('POST', destinationURI, true);
// Set 2 Request headers, based on browser
if (actionURI == '') {
SOAPObject.setRequestHeader('SOAPAction', '\"\"');
} else { SOAPObject.setRequestHeader('SOAPAction', actionURI);
}
SOAPObject.setRequestHeader('Content-Type', 'text/xml');
// Compose the Request body from input parameter + global variables
var requestBody = soapHeader + parms + soapFooter
// Send, based on browser
if (navigator.userAgent.indexOf('Netscape') != -1)
{
SOAPObject.send(new DOMParser().parseFromString(requestBody,'text/xml'));
} else {
SOAPObject.send(requestBody);
}
} catch (E)
{
alert('callWebService exception: ' + E);
}
}