crm2011 plugin call js function - javascript

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?

Related

Validate if a file exists with JavaScript

Good day. I need to know if the file that I indicate in the path that I save in the text variable "t" as the slide variable "s" exist, in order to be able to show them or not through the view. I need to do this with only java script and local files, no server side. If possible, I would be very grateful to receive your help.
My current code in JavaScript:
function loadFiles(num) {
let s = 'assets/content/Compiladores/Slides/' + num + '.jpg';
let t = 'assets/content/Compiladores/Texts/' + num + '.txt';
document.slider.src = s;
$("#description").load(t);
$("#num").text(num);
}
You are never going to be able to reliably determine if a resource exists before doing something with it.
Note that this holds true even on a program that runs directly on a user's machine or a server. I don't normally mention other people's answers in my own, but the one advocating that you do that check (which per the link is problematic even in the best of circumstances) but across a client-server gap is sheer folly. Think about all the things that can go wrong:
The server could have moved the resource between the time you check and the time you set it.
Another thread or even entire other program could have acquired a lock on the resource causing your request to either take a lot of extra time or fail outright.
The user could be on a mobile connection and lost signal between the time of the check and the time of the resource request.
If you're passing an authentication token the user's session could expire between the time of the check and the time of the actual request.
The server could crash between the first request and the second.
The second request could be load-balanced to a different server than the first request.
The entire network for a major service could go down between the requests. Yes, really.
And so on.
Not to mention that for the happy path it involves a completely unnecessary extra round trip to the server before you start showing them the thing you wanted to show them in the first place!
The correct way to do this is to attempt to load the resource and then just handle the error if it fails:
function loadFiles(num) {
let s = 'assets/content/Compiladores/Slides/' + num + '.jpg';
let t = 'assets/content/Compiladores/Texts/' + num + '.txt';
document.slider.onerror = function () {
// deal with the resource not loading here
}
document.slider.src = s;
const desc = $("#description");
desc.on("error" function () {
// deal with the resource not loading here
});
desc.load(t);
$("#num").text(num);
}
try this for the file exists or not
function UrlExists(url)
{
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
return http.status!=404;
}
function UrlExists(url)
{
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
return http.status!=404;
}
function loadFiles(num) {
let s = 'assets/content/Compiladores/Slides/' + num + '.jpg';
let t = 'assets/content/Compiladores/Texts/' + num + '.txt';
document.slider.src = s;
if(UrlExists(s)){
$("#description").load(t);
}
if(UrlExists(t)){
$("#num").text(num);
}
}

response.getselectedbutton() is not a function?

I am in school and trying to code a google apps script where I have a function bound to a doc with a custom menu that can create a new doc with the date, subject, and email it to the teacher of said subject. All was going according to plan until I decided to make it so that if I hit cancel instead of OK in one of the parameters I would get the default value of that parameter if I had not hit edit parameters so I could easily only change 1 or 2 things. The function works spotlessly, but now what's happening is I can't get the selected button for the alert that asks me if I want to edit parameters.
It just says
"TypeError: response.getSelectedButton is not a function"
and won't let me run my function. Here's my code for the function. This has worked fine for me in the past and I'm really not sure why it doesn't work now.
function Mathdoc() { var ui = DocumentApp.getUi();
var d = new Date();
var s = (d.getDate()) + '/' + (d.getMonth()+1) + '/' + d.getFullYear();
console.log(s);
var response = ui.alert( 'Change parameters?', ui.ButtonSet.YES_NO);
if (response.getSelectedButton() == ui.Button.YES) { insert all my other code here}
Is there a glitch or something I'm missing? I'm new to JS and fairly new to web design altogether.
There is no method named getSelectedButton(). The ui.alert() just returns the button and nothing else
function Mathdoc() {
var ui=DocumentApp.getUi();
var d = new Date();
var s = (d.getDate()) + '/' + (d.getMonth()+1) + '/' + d.getFullYear();
console.log(s);
var response=ui.alert( 'Change parameters?', ui.ButtonSet.YES_NO);
if (response==ui.Button.YES) { insert all my other code here}
Look at example here
Nope, this function doesn't exist. Basically if you type a function on google and don't find anything, it means the function doesn't exist
EDIT : If you have an error message that says it doesn't exist, it means that the function really doesn't exist or that the library containing the function is missing. But in your case the function doesn't exist

Start workflow with javascript in Dynamics CRM 2016

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");

Javascript Ajax save result of onreadystatechange in global variable

I'm trying to write a script to retrieve a WOEID and interface with the Yahoo Weather API. I'm constructing a URL based on latitude and longitude values from stuff in a database I'm using, and can do this perfectly fine.
However, when it comes to storing that URL as a string that I can use in other functions, I'm having trouble. After some initial reading it seems to be a problem to do with onreadystatechange and scope, but I can't seem to get my head around it to be able to store my variable.
Here is my code so far:
//<![CDATA[
var latitude = "";
var longitude = "";
var yahooAppID = "";
var yql = "";
//example yahoo request
//http://where.yahooapis.com/geocode?q=38.898717,+-77.035974&gflags=R&appid=SKUTk24k
function getLatLng() {
var routeID = 5;
var get = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
get.open('POST','process.php', true)
get.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
get.send('command=fetch&rid='+routeID);
get.onreadystatechange = function(){
if(get.readyState==4 && get.status == 200) {
os = eval('(' + get.responseText + ')');
latitude = os.start.lat;
longitude = os.start.lng;
//var yql = 'select * from flickr.places where lat='+latitude+' and lon='+longitude;
yql = "select * from flickr.places where lat=" +latitude+ " and lon="+longitude;
}
document.write(yql);
}
document.write(yql);
}
function test() {
getLatLng();
}
//]]>
The first document.write(yql); seems to produce the correct string, but the second does not, so I know that the value has not stuck.
Thanks in advance if anyone can help.
You're not looking at a scoping issue — rather, a timing issue. AJAX requests (at least the way you've configured it) happen asynchronously, so the rest of your script will execute while the AJAX request is loading. So yql won't be updated until the very end.
Also, warning: by the time the request completes, you can no longer use document.write. Use alert() or DOM manipulation.
Life cycle looks like this:
Script is executed normally
Anonymous function is bound to onreadystatechange
readyState changes from "unsent" to "headers received" to "loading" to "done". Your function gets called every time, but you only set yql when the ready state is "done."
readyState is done, and you generate yql.
So, just call your processing function from inside the if:
if(get.readyState==4 && get.status == 200) {
os = get.responseXML;
//find lat + lng
yql = "select * from flickr.places where lat=" +latitude+ " and lon="+longitude;
process(yql); //here!
}
function process(yql) {
alert(yql); //do something more useful eventually
}
jsFiddle

Update DOM Elements Immediately in Javascript

For a simple project, I'm trying to display log output in a textarea. I have a log function, and would like the textarea to always be updated immediately whenever log is called. Unfortunately, it seems to only do the update once, and push everything at the same time (I'm using JQuery to set the value of the textarea, if that possibly matters). Here's a basic example:
for (i = 0; i <= 100; i++) {
$("textarea").html($("textarea").html() + "test" + i + "\n");
$("textarea").scrollTop(9999999)
};
This spits out all the text into the textarea at once (note: you can see the results of these examples at this jsfiddle). This basic example is easily remedied by creating a timeout and using recursive function calls:
f = function(i) {
if (i <= 100) {
$("textarea").html($("textarea").html() + "test" + i + "\n");
$("textarea").scrollTop(999999);
setTimeout(function() { f(i+1); }, 0);
}
};
f(1);
This version spits out the text into the textarea one line at a time, which is what I want. But using timeouts and callbacks in this manner does not seem practical in the setting of logging; every time I call log, I would have to provide a callback for all the functionality that I want to ever follow the log call.
Is there any way to achieve the desired effect without callbacks?
I think you might consider using :
$("textarea").val(function(index, value) {
return value + "test" + i + "\n"
});
instead of :
$("textarea").html($("textarea").html() + "test" + i + "\n");
or in general :
$("textarea").val(NEWVAL)
Also noted in the comments of your question, if you want to be able to notice "by eye" all the messages that arrives you'll have to save them in a buffer and have something like (not tested) :
var buffer = []
function log(text) { buffer.push(text) }
setInterval(function(){
if (len(buffer)>0) {
$("textarea").val(function(index, value) {
return value + "\n" + buffer.shift()
});
}
},500)
Browsers generally (Opera is an exception) do JS execution and rendering on the same thread, so while your JS is running there will be no rendering done by th browser, unless you explicitly yield control via a timeout or interval timer.

Categories

Resources