Hi all, Today I am going to post a sample code you can use to run a workflow in CRM 365 using the new Web Api.
The function takes three parameters in input: the workflow Guid, the record Guid and a boolean to tell the Web Api whether or not to run the workflow synchronously; true = asynchronously which is the default behaviour, false = synchronously.
The Workflow should be set up to be run either as a child or On demand Workflow otherwise you'll get an error.
var _runWorkflow = function (workflowId, recordId) {
var parameters = {};
parameters.EntityId = recordId;
var req = new XMLHttpRequest();
req.open("POST", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/workflows(" + workflowId + ")/Microsoft.Dynamics.CRM.ExecuteWorkflow", false);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var results = JSON.parse(this.response);
} else {
//Xrm.Utility.alertDialog(this.statusText);
}
}
};
req.send(JSON.stringify(parameters));
}
Here is an example of how to call the function above after saving the record via javascript:
Xrm.Page.data.save().then(
function () {
var entityType = Xrm.Page.data.entity.getEntityName();
var recordId = Xrm.Page.data.entity.getId();
var wfGuid = LDA.Functions.Common.GetWorkflowGuid(entityType);
if (wfGuid == "na") return;
LDA.Functions.Common.RunWorkflow(wfGuid, recordId.replace("}", "").replace("{", ""));
Xrm.Page.data.refresh(false);
},
function () {
//handle errors
}
);
That was it, very simple and straight forward, hope it helps to get you started with the new CRM 365 Web Api. If you have any questions leave a comment and I'll get back to you ASAP.
A Blog about my experience with Microsoft Dynamics CRM as developer.
Featured Post
Web API Requests Series
Web API Series: Create and Retrieve , Update and Delete , Retrieve Multiple , Associate/Disassociate , Impersonation , Run Workflows
03 February 2017
07 April 2016
Activities Timeline
I have recently been playing with a very nice HTML Timeline control and have integrated within the Account form in CRM using the new Web API, of course! The Timeline is used to display activities related to the current Account record, it could be used on any record type though.
Here is a screenshot of the beta version:
Some of the functionalities so far:
- Add new activities (phone calls, emails, tasks, appointments)
- Drag to reschedule activities
- Select activities to display
- Zoom in and out to adjust the period displayed
- Refresh view
- Go to Today's activities
04 March 2016
Web API Requests Series
Web API Series: Create and Retrieve, Update and Delete, Retrieve Multiple, Associate/Disassociate, Impersonation, Run Workflows
Labels:
associate,
Create,
crm 2016,
CRUD,
delete,
impersonate,
Retrieve,
sample code,
Update,
web api
CRM 2016 - WEB API Operations - Associate/Disassociate for Many to Many
Associate and Disassociate for Many to Many (i.e. Collection-Valued navigation property) relationships using the new CRM 2016 Web API in Javascript.
In this example I am going to show you how to Associate/Disassociate two out of the box records: an opportunity and an account.
You can find more information about Single and Collection-Valued properties here and here
this.associateRecords = function (parentId, parentType, relationshipName, childId, childType, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("POST", encodeURI(getWebAPIPath() + parentType + "(" + parentId + ")/" + relationshipName + "/$ref"), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
var childEntityReference = { "@odata.id": getWebAPIPath() + "/" + childType + "(" + childId + ")" };
req.send(JSON.stringify(childEntityReference));
};
//Account: E48456EE-49B0-E611-810F-3863BB357C39
//Opportunity: F45D2DF7-6DBF-E511-8109-3863CB343C91
associate: function () {
bd_Utilities.WebAPI.associateRecords("E48333EE-49B9-E511-810F-3863BB357C38", "accounts", "opportunity_customer_accounts", "F25D2DF7-6CBF-E511-8109-3863BB343C90", "opportunities",
function () {
},
function () {
});
}
And the Disassociate
this.disassociateRecords = function (parentId, parentType, relationshipName, childId, childType, successCallback, errorCallback) {
var req = new XMLHttpRequest();
var webApiPath = getWebAPIPath();
var parentUri = webApiPath + "/" + parentType + "(" + parentId + ")/";
var childUri = webApiPath + "/" + childType + "(" + childId + ")";
req.open("DELETE", encodeURI(parentUri + relationshipName + "/$ref?$id=" + childUri), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send(JSON.stringify());
};
function getWebAPIPath() {
return getClientUrl() + "/api/data/v8.0/";
}
function getClientUrl() {
//Get the organization URL
if (typeof GetGlobalContext == "function" &&
typeof GetGlobalContext().getClientUrl == "function") {
return GetGlobalContext().getClientUrl();
}
else {
//If GetGlobalContext is not defined check for Xrm.Page.context;
if (typeof Xrm != "undefined" &&
typeof Xrm.Page != "undefined" &&
typeof Xrm.Page.context != "undefined" &&
typeof Xrm.Page.context.getClientUrl == "function") {
try {
return Xrm.Page.context.getClientUrl();
} catch (e) {
throw new Error("Xrm.Page.context.getClientUrl is not available.");
}
}
else { throw new Error("Context is not available."); }
}
}
In this example I am going to show you how to Associate/Disassociate two out of the box records: an opportunity and an account.
You can find more information about Single and Collection-Valued properties here and here
this.associateRecords = function (parentId, parentType, relationshipName, childId, childType, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("POST", encodeURI(getWebAPIPath() + parentType + "(" + parentId + ")/" + relationshipName + "/$ref"), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
var childEntityReference = { "@odata.id": getWebAPIPath() + "/" + childType + "(" + childId + ")" };
req.send(JSON.stringify(childEntityReference));
};
//Account: E48456EE-49B0-E611-810F-3863BB357C39
//Opportunity: F45D2DF7-6DBF-E511-8109-3863CB343C91
associate: function () {
bd_Utilities.WebAPI.associateRecords("E48333EE-49B9-E511-810F-3863BB357C38", "accounts", "opportunity_customer_accounts", "F25D2DF7-6CBF-E511-8109-3863BB343C90", "opportunities",
function () {
},
function () {
});
}
And the Disassociate
this.disassociateRecords = function (parentId, parentType, relationshipName, childId, childType, successCallback, errorCallback) {
var req = new XMLHttpRequest();
var webApiPath = getWebAPIPath();
var parentUri = webApiPath + "/" + parentType + "(" + parentId + ")/";
var childUri = webApiPath + "/" + childType + "(" + childId + ")";
req.open("DELETE", encodeURI(parentUri + relationshipName + "/$ref?$id=" + childUri), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send(JSON.stringify());
};
function getWebAPIPath() {
return getClientUrl() + "/api/data/v8.0/";
}
function getClientUrl() {
//Get the organization URL
if (typeof GetGlobalContext == "function" &&
typeof GetGlobalContext().getClientUrl == "function") {
return GetGlobalContext().getClientUrl();
}
else {
//If GetGlobalContext is not defined check for Xrm.Page.context;
if (typeof Xrm != "undefined" &&
typeof Xrm.Page != "undefined" &&
typeof Xrm.Page.context != "undefined" &&
typeof Xrm.Page.context.getClientUrl == "function") {
try {
return Xrm.Page.context.getClientUrl();
} catch (e) {
throw new Error("Xrm.Page.context.getClientUrl is not available.");
}
}
else { throw new Error("Context is not available."); }
}
}
CRM 2016 - WEB API CRUD Operations - Impersonate another User
Web API series Part 1, Part 2, Part 3
Impersonating another user is very simple, you just need to set the XMLHttpRequest header as shown below.
Here are a couple of basic examples on how to impersonate a User in CRUD operations with the new CRM 2016 Web API in Javascript, here I am just going to show you impersonation in Create and Update. Impersonating another user should also be possible in Delete and Retrieve operations. I haven't tried it yet.
updateRecordOnBehalfOf = function (id, object, entitySetName, successCallback, errorCallback, impersonatedUserId) {
var req = new XMLHttpRequest();
req.open("PATCH", encodeURI(getWebAPIPath() + entitySetName + "(" + id + ")"), true);
req.setRequestHeader("MSCRMCallerID", impersonatedUserId);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send(JSON.stringify(object));
};
createOnBehalfOf = function (entitySetName, entity, successCallback, errorCallback, impersonatedUserId) {
var req = new XMLHttpRequest();
req.open("POST", encodeURI(getWebAPIPath() + entitySetName), true);
req.setRequestHeader("MSCRMCallerID", impersonatedUserId);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204) {
if (successCallback)
successCallback(this.getResponseHeader("OData-EntityId"));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send(JSON.stringify(entity));
};
I hope this help you getting started with the new CRM 2016 Web API.
Impersonating another user is very simple, you just need to set the XMLHttpRequest header as shown below.
Here are a couple of basic examples on how to impersonate a User in CRUD operations with the new CRM 2016 Web API in Javascript, here I am just going to show you impersonation in Create and Update. Impersonating another user should also be possible in Delete and Retrieve operations. I haven't tried it yet.
updateRecordOnBehalfOf = function (id, object, entitySetName, successCallback, errorCallback, impersonatedUserId) {
var req = new XMLHttpRequest();
req.open("PATCH", encodeURI(getWebAPIPath() + entitySetName + "(" + id + ")"), true);
req.setRequestHeader("MSCRMCallerID", impersonatedUserId);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send(JSON.stringify(object));
};
createOnBehalfOf = function (entitySetName, entity, successCallback, errorCallback, impersonatedUserId) {
var req = new XMLHttpRequest();
req.open("POST", encodeURI(getWebAPIPath() + entitySetName), true);
req.setRequestHeader("MSCRMCallerID", impersonatedUserId);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204) {
if (successCallback)
successCallback(this.getResponseHeader("OData-EntityId"));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send(JSON.stringify(entity));
};
I hope this help you getting started with the new CRM 2016 Web API.
02 March 2016
CRM 2016 - WEB API CRUD Operations (Part 3)
Following Part 1 and Part 2 here is how to "Update" and "Delete" using the new CRM WebAPI.
this.updateRecord = function (id, object, entitySetName, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("PATCH", encodeURI(getWebAPIPath() + type + "(" + id + ")"), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send(JSON.stringify(object));
};
this.deleteRecord = function (id, entitySetName, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("DELETE", encodeURI(getWebAPIPath() + type + "(" + id + ")", true));
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send();
};
this.updateRecord = function (id, object, entitySetName, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("PATCH", encodeURI(getWebAPIPath() + type + "(" + id + ")"), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send(JSON.stringify(object));
};
this.deleteRecord = function (id, entitySetName, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("DELETE", encodeURI(getWebAPIPath() + type + "(" + id + ")", true));
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send();
};
CRM 2016 - WEB API CRUD Operations (Part 2)
Retrieving multiple records using paging with the new CRM 2016 Web API in Javascript
On my previous post I talked about how to "Retrieve" a single record by GUID and how to create a record.
In this post I will show you how to perform a simple "RETRIEVEMULTIPLE" using the new CRM Web API.
Straight to the code, which you can add to the other functions from my previous post.
The highlighted code is necessary to achieve paging in cases where we want to, for whatever reason, or when there are more than 5000 records in the result set.
(Note that is different from the previous implementation of the OData service)
If you do not want to do paging and you have more than 5000 records remove this line:
"req.setRequestHeader("Prefer", "odata.maxpagesize=500");" and every page will contain 5000 records not 50 as in the previous OData service.
this.retrieveMultipleRecords = function (entitySetName, select, successCallback, errorCallback, onComplete) {
var req = new XMLHttpRequest();
//This part works, but I am not sure if it is the best way
if (select.indexOf("skiptoken") != -1) {
req.open("GET", select, true);
}
else {
req.open("GET", encodeURI(getWebAPIPath() + entitySetName + select), true);
}
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Prefer", "odata.maxpagesize=500");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
var returned = JSON.parse(this.responseText, bd_Utilities.WebAPI.dateReviver);
if (successCallback) {
successCallback(returned);
}
if (returned["@odata.nextLink"] != null) {
var queryOptions = returned["@odata.nextLink"];
bd_Utilities.WebAPI.retrieveMultipleRecords(entitySetName, queryOptions, successCallback, errorCallback, onComplete);
}
else {
onComplete();
}
}
else {
if (errorCallback) {
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
}
};
req.send();
};
And here is the function call:
retrieveMultiple: function () {
"use strict";
bd_Utilities.WebAPI.retrieveMultipleRecords("new_events", "?$select=new_name",
function (results) {
events = events.concat(results.value);
},
function (error) { console.log(error.message); },
function () {
console.log("Retrieve multiple complete!");
});
},
01 March 2016
CRM 2016 - WEB API CRUD Operations (Part 1)
How to CRUD with the new CRM 2016 Web API in Javascript
In this post I wanted to talk of the new Web API available in CRM 2016. I am not sure about you but I could not find any example, other than the basic Create operation provided by Microsoft, of how to use the new Web API for CRUD operations.
So here it is an example of a RETRIEVE (and the CREATE from the Microsoft example found here) query using the Web API, I will be adding more examples of "Retrieve Multiple, "Update" and "Delete" as I discover how to implement them.
"use strict";
var bd_Utilities = window.bd_Utilities || {};
bd_Utilities.WebAPI = bd_Utilities.WebAPI || {};
(function () {
this.create = function (entitySetName, entity, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("POST", encodeURI(getWebAPIPath() + entitySetName), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204) {
if (successCallback)
successCallback(this.getResponseHeader("OData-EntityId"));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send(JSON.stringify(entity));
};
this.retrieve = function (id, entitySetName, select, expand, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("GET", encodeURI(getWebAPIPath() + entitySetName + "(" + id + ")" + select), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
if (successCallback)
successCallback(JSON.parse(this.responseText, bd_Utilities.WebAPI.dateReviver));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send();
};
//This is the calling function:
search: function () {
bd_Utilities.WebAPI.retrieve("00000000-0000-0000-0000-000000000090", "contacts", "?$select=firstname,lastname", "",
function (result) {
var contact = result;
console.log("Contact retrieved")
},
function (error) { console.log(error.message); });
}
//And these are some helper functions from the CRM SDK
//Internal supporting functions
function getClientUrl() {
//Get the organization URL
if (typeof GetGlobalContext == "function" &&
typeof GetGlobalContext().getClientUrl == "function") {
return GetGlobalContext().getClientUrl();
}
else {
//If GetGlobalContext is not defined check for Xrm.Page.context;
if (typeof Xrm != "undefined" &&
typeof Xrm.Page != "undefined" &&
typeof Xrm.Page.context != "undefined" &&
typeof Xrm.Page.context.getClientUrl == "function") {
try {
return Xrm.Page.context.getClientUrl();
} catch (e) {
throw new Error("Xrm.Page.context.getClientUrl is not available.");
}
}
else { throw new Error("Context is not available."); }
}
}
function getWebAPIPath() {
return getClientUrl() + "/api/data/v8.0/";
}
//Internal validation functions
function isString(obj) {
if (typeof obj === "string") {
return true;
}
return false;
}
function isNull(obj) {
if (obj === null)
{ return true; }
return false;
}
function isUndefined(obj) {
if (typeof obj === "undefined") {
return true;
}
return false;
}
function isFunction(obj) {
if (typeof obj === "function") {
return true;
}
return false;
}
function isNullOrUndefined(obj) {
if (isNull(obj) || isUndefined(obj)) {
return true;
}
return false;
}
function isFunctionOrNull(obj) {
if (isNull(obj))
{ return true; }
if (isFunction(obj))
{ return true; }
return false;
}
// This function is called when an error callback parses the JSON response
// It is a public function because the error callback occurs within the onreadystatechange
// event handler and an internal function would not be in scope.
this.errorHandler = function (resp) {
try {
return JSON.parse(resp).error;
} catch (e) {
return new Error("Unexpected Error")
}
}
this.dateReviver = function (key, value) {
///<summary>
/// Private function to convert matching string values to Date objects.
///</summary>
///<param name="key" type="String">
/// The key used to identify the object property
///</param>
///<param name="value" type="String">
/// The string value representing a date
///</param>
var a;
if (typeof value === 'string') {
a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));
}
}
return value;
}
}).call(bd_Utilities.WebAPI);
In this post I wanted to talk of the new Web API available in CRM 2016. I am not sure about you but I could not find any example, other than the basic Create operation provided by Microsoft, of how to use the new Web API for CRUD operations.
So here it is an example of a RETRIEVE (and the CREATE from the Microsoft example found here) query using the Web API, I will be adding more examples of "Retrieve Multiple, "Update" and "Delete" as I discover how to implement them.
"use strict";
var bd_Utilities = window.bd_Utilities || {};
bd_Utilities.WebAPI = bd_Utilities.WebAPI || {};
(function () {
this.create = function (entitySetName, entity, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("POST", encodeURI(getWebAPIPath() + entitySetName), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204) {
if (successCallback)
successCallback(this.getResponseHeader("OData-EntityId"));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send(JSON.stringify(entity));
};
this.retrieve = function (id, entitySetName, select, expand, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("GET", encodeURI(getWebAPIPath() + entitySetName + "(" + id + ")" + select), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
if (successCallback)
successCallback(JSON.parse(this.responseText, bd_Utilities.WebAPI.dateReviver));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send();
};
//This is the calling function:
search: function () {
bd_Utilities.WebAPI.retrieve("00000000-0000-0000-0000-000000000090", "contacts", "?$select=firstname,lastname", "",
function (result) {
var contact = result;
console.log("Contact retrieved")
},
function (error) { console.log(error.message); });
}
//And these are some helper functions from the CRM SDK
//Internal supporting functions
function getClientUrl() {
//Get the organization URL
if (typeof GetGlobalContext == "function" &&
typeof GetGlobalContext().getClientUrl == "function") {
return GetGlobalContext().getClientUrl();
}
else {
//If GetGlobalContext is not defined check for Xrm.Page.context;
if (typeof Xrm != "undefined" &&
typeof Xrm.Page != "undefined" &&
typeof Xrm.Page.context != "undefined" &&
typeof Xrm.Page.context.getClientUrl == "function") {
try {
return Xrm.Page.context.getClientUrl();
} catch (e) {
throw new Error("Xrm.Page.context.getClientUrl is not available.");
}
}
else { throw new Error("Context is not available."); }
}
}
function getWebAPIPath() {
return getClientUrl() + "/api/data/v8.0/";
}
//Internal validation functions
function isString(obj) {
if (typeof obj === "string") {
return true;
}
return false;
}
function isNull(obj) {
if (obj === null)
{ return true; }
return false;
}
function isUndefined(obj) {
if (typeof obj === "undefined") {
return true;
}
return false;
}
function isFunction(obj) {
if (typeof obj === "function") {
return true;
}
return false;
}
function isNullOrUndefined(obj) {
if (isNull(obj) || isUndefined(obj)) {
return true;
}
return false;
}
function isFunctionOrNull(obj) {
if (isNull(obj))
{ return true; }
if (isFunction(obj))
{ return true; }
return false;
}
// This function is called when an error callback parses the JSON response
// It is a public function because the error callback occurs within the onreadystatechange
// event handler and an internal function would not be in scope.
this.errorHandler = function (resp) {
try {
return JSON.parse(resp).error;
} catch (e) {
return new Error("Unexpected Error")
}
}
this.dateReviver = function (key, value) {
///<summary>
/// Private function to convert matching string values to Date objects.
///</summary>
///<param name="key" type="String">
/// The key used to identify the object property
///</param>
///<param name="value" type="String">
/// The string value representing a date
///</param>
var a;
if (typeof value === 'string') {
a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));
}
}
return value;
}
}).call(bd_Utilities.WebAPI);
Labels:
2015,
crm 2016,
crm 2016 online,
CRUD,
examples,
luciano d'angelo,
luciano dangelo,
odata,
odata 4.0,
online,
REST,
retrieve multiple,
sample code,
web api,
web api create,
web api javascript,
web api retrieve
25 February 2015
How to use the ExecuteMultipleRequest to resolve cases
This post is about how to use the "ExecuteMultipleRequest" message to resolve cases in Bulk.
The main advantage is that if you have to update thousands of record you can do that by sending one single server request and not thousands of requests as it happens with the single Request.
This particular example is for changing the state and status of cases. To achieve that you have to use the "CloseIncidentRequest" message.
The "ExecuteMultipleRequest" message allows us to create a collection of "CloseIncidentRequest" requests and send it to the server to be processed.
Let's start off by creating a "ExecuteMultipleRequest" object:
var myMultipleRequest = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = true,
ReturnResponses = false
},
Requests = new OrganizationRequestCollection()
};
And then we can loop through our list of cases to create a "CloseIncidentRequest" for each case.
I used a helper method to build the CloseIncidentRequest called "BuildIncidentResolutionRequest"
which takes the incident id and the status reason as input:
foreach (var incident in cases.Entities)
{
var closeIncidentRequest = BuildIncidentResolutionRequest(incident.Id, caseStatusReason);
myMultipleRequest.Requests.Add(closeIncidentRequest);
}
This method first creates a new Entity (which is an activity of type Case Resolution) called "incidentresolution" which will be used in the "CloseIncidentRequest" this entity is used to resolve the incident and a subject and description can be provided.
Note: If you try to resolve a case using the UI you must enter a resolution (which is the subject) that information will be held in the entity called "incidentresolution" which can be viewed in CRM with an advanced find or in the closed activities linked to the case. An "incidentresolution" record will be created for each case you are resolving.
It then creates the "CloseIncidentRequest".
private CloseIncidentRequest BuildIncidentResolutionRequest(Guid incidentId, int caseStatusReason)
{
// Create the incident's resolution.
var incidentResolution = new Entity("incidentresolution");
incidentResolution["subject"] = "Case was Resolved";
incidentResolution["incidentid"] = new EntityReference("incident", incidentId);
incidentResolution["description"] = "Description";
// Close the incident with the resolution.
var closeIncidentRequest = new CloseIncidentRequest
{
IncidentResolution = incidentResolution,
Status = new OptionSetValue(caseStatusReason)
};
return closeIncidentRequest;
}
...and finally the list of requests is sent to the server to be processed.
var responses = (ExecuteMultipleResponse)orgService.Execute(myMultipleRequest);
We can also assert if any error occurred by checking the "IsFaulted" flag.
Anyway for more information refer to the msdn this page.
if (responses.IsFaulted)
{
Console.WriteLine("Error: {0}", myMultipleRequest.Responses[0].Fault.Message);
}
The main advantage is that if you have to update thousands of record you can do that by sending one single server request and not thousands of requests as it happens with the single Request.
This particular example is for changing the state and status of cases. To achieve that you have to use the "CloseIncidentRequest" message.
The "ExecuteMultipleRequest" message allows us to create a collection of "CloseIncidentRequest" requests and send it to the server to be processed.
Let's start off by creating a "ExecuteMultipleRequest" object:
var myMultipleRequest = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = true,
ReturnResponses = false
},
Requests = new OrganizationRequestCollection()
};
And then we can loop through our list of cases to create a "CloseIncidentRequest" for each case.
I used a helper method to build the CloseIncidentRequest called "BuildIncidentResolutionRequest"
which takes the incident id and the status reason as input:
foreach (var incident in cases.Entities)
{
var closeIncidentRequest = BuildIncidentResolutionRequest(incident.Id, caseStatusReason);
myMultipleRequest.Requests.Add(closeIncidentRequest);
}
This method first creates a new Entity (which is an activity of type Case Resolution) called "incidentresolution" which will be used in the "CloseIncidentRequest" this entity is used to resolve the incident and a subject and description can be provided.
Note: If you try to resolve a case using the UI you must enter a resolution (which is the subject) that information will be held in the entity called "incidentresolution" which can be viewed in CRM with an advanced find or in the closed activities linked to the case. An "incidentresolution" record will be created for each case you are resolving.
It then creates the "CloseIncidentRequest".
private CloseIncidentRequest BuildIncidentResolutionRequest(Guid incidentId, int caseStatusReason)
{
// Create the incident's resolution.
var incidentResolution = new Entity("incidentresolution");
incidentResolution["subject"] = "Case was Resolved";
incidentResolution["incidentid"] = new EntityReference("incident", incidentId);
incidentResolution["description"] = "Description";
// Close the incident with the resolution.
var closeIncidentRequest = new CloseIncidentRequest
{
IncidentResolution = incidentResolution,
Status = new OptionSetValue(caseStatusReason)
};
return closeIncidentRequest;
}
...and finally the list of requests is sent to the server to be processed.
var responses = (ExecuteMultipleResponse)orgService.Execute(myMultipleRequest);
We can also assert if any error occurred by checking the "IsFaulted" flag.
Anyway for more information refer to the msdn this page.
if (responses.IsFaulted)
{
Console.WriteLine("Error: {0}", myMultipleRequest.Responses[0].Fault.Message);
}
04 July 2012
How to associate a record as Activity Party to an activity.
In this post I will show you how to associate a record to an activity (in this case an email) as an Activity Party using the OData REST end point with Javascript.
Setting the "To", "From", "Cc", "Bcc" fields on an email message is not so straight forward as other lookup fields such as the "Regarding" field. This is because these fields are of Activity Party type.
There are two important steps to follow:
Use: activityParty.ParticipationTypeMask = { Value: 1 }; To set the "From" field
Use: activityParty.ParticipationTypeMask = { Value: 2 }; To set the "To" field
Use: activityParty.ParticipationTypeMask = { Value: 3 }; To set the "Cc" field
Use: activityParty.ParticipationTypeMask = { Value: 4 }; To set the "Bcc" fiel.
Ultimately perform the REST request.
Here is the complete code:
createActivityParty: function (object, email, logicalName, recipient) {
var activityParty = {}, jsonActivityParty;
switch (logicalName) {
case "account":
activityParty.PartyId = {
Id: object.AccountId,
LogicalName: "account"
};
break;
case "contact":
activityParty.PartyId = {
Id: object.ContactId,
LogicalName: "contact"
};
break;
case "systemUser":
activityParty.PartyId = {
Id: object.SystemUserId,
LogicalName: "systemuser"
};
break;
default:
}
// Set the "activity" of the ActivityParty (the e-mail, in this case) as an EntityReference.
activityParty.ActivityId = {
Id: email.ActivityId,
LogicalName: "email"
};
// Set the participation type (what role the party has on the activity). For this
// example, we'll put the account in the From field (which has a value of 1).
if (recipient == "sender") {
activityParty.ParticipationTypeMask = { Value: 1 };
}
else if (recipient == "to") {
activityParty.ParticipationTypeMask = { Value: 2 };
}
else if (recipient == "cc") {
activityParty.ParticipationTypeMask = { Value: 3 };
}
//stringify the object to be created
jsonActivityParty = JSON.stringify(activityParty);
this._performRequest({
type: "POST",
url: this._ODataPath() + "/ActivityPartySet",
data: jsonActivityParty,
success: function (request) {
},
error: function (request) {
this._errorHandler(request);
}
});
},
Hope this helps, also don't forget to check the SDK. which is where I learned about this.
Setting the "To", "From", "Cc", "Bcc" fields on an email message is not so straight forward as other lookup fields such as the "Regarding" field. This is because these fields are of Activity Party type.
There are two important steps to follow:
- Set the "party" of the ActivityParty (what will be related to the activity) as an EntityReference. activityParty.PartyId = { Id: record.RecordId, LogicalName: "logicalName" };
- Set the "activity" of the ActivityParty (the e-mail, in this case) as an EntityReference. activityParty.ActivityId = { Id: email.ActivityId, LogicalName: "email" };
Use: activityParty.ParticipationTypeMask = { Value: 1 }; To set the "From" field
Use: activityParty.ParticipationTypeMask = { Value: 2 }; To set the "To" field
Use: activityParty.ParticipationTypeMask = { Value: 3 }; To set the "Cc" field
Use: activityParty.ParticipationTypeMask = { Value: 4 }; To set the "Bcc" fiel.
Ultimately perform the REST request.
Here is the complete code:
createActivityParty: function (object, email, logicalName, recipient) {
var activityParty = {}, jsonActivityParty;
switch (logicalName) {
case "account":
activityParty.PartyId = {
Id: object.AccountId,
LogicalName: "account"
};
break;
case "contact":
activityParty.PartyId = {
Id: object.ContactId,
LogicalName: "contact"
};
break;
case "systemUser":
activityParty.PartyId = {
Id: object.SystemUserId,
LogicalName: "systemuser"
};
break;
default:
}
// Set the "activity" of the ActivityParty (the e-mail, in this case) as an EntityReference.
activityParty.ActivityId = {
Id: email.ActivityId,
LogicalName: "email"
};
// Set the participation type (what role the party has on the activity). For this
// example, we'll put the account in the From field (which has a value of 1).
if (recipient == "sender") {
activityParty.ParticipationTypeMask = { Value: 1 };
}
else if (recipient == "to") {
activityParty.ParticipationTypeMask = { Value: 2 };
}
else if (recipient == "cc") {
activityParty.ParticipationTypeMask = { Value: 3 };
}
//stringify the object to be created
jsonActivityParty = JSON.stringify(activityParty);
this._performRequest({
type: "POST",
url: this._ODataPath() + "/ActivityPartySet",
data: jsonActivityParty,
success: function (request) {
},
error: function (request) {
this._errorHandler(request);
}
});
},
Hope this helps, also don't forget to check the SDK. which is where I learned about this.
13 April 2012
How to assign a record to another User in CRM 2011
In this brief post I will show you how to set the Owner of a record in C#, in other words how to assign a record to a User.
To assign a record an Owner you have to send an Assign Request using the Execute method.
service.Execute(new AssignRequest()
{
Assignee = new EntityReference("systemuser", userId),
Target = new EntityReference(updateEntity, recordId)
}
);
The Assign Request has two properties:
Hope this helps.
To assign a record an Owner you have to send an Assign Request using the Execute method.
service.Execute(new AssignRequest()
{
Assignee = new EntityReference("systemuser", userId),
Target = new EntityReference(updateEntity, recordId)
}
);
The Assign Request has two properties:
- Assignee: This can be either a Team or a User.
- Target: this is the record you want to assign.
Hope this helps.
03 April 2012
Open CRM form via JavaScript
Have you been struggling to find out how to open a CRM form from another form or from a custom page?
Here is the solution:
Use window.open("/main.aspx?etn=entityname&pagetype=entityrecord&id=" + recordGUID");
or simply:
window.open("/main.aspx?etn=entityname&pagetype=entityrecord");
to create a new record.
You can also pass custom parameters using the extraqs parameter and encodeURIComponent. You can find out more from the Crm SDK.
Cheers.
Here is the solution:
Use window.open("/main.aspx?etn=entityname&pagetype=entityrecord&id=" + recordGUID");
or simply:
window.open("/main.aspx?etn=entityname&pagetype=entityrecord");
to create a new record.
You can also pass custom parameters using the extraqs parameter and encodeURIComponent. You can find out more from the Crm SDK.
Cheers.
25 March 2012
Read only fields are not saved.
If you are writing your custom web resource and using it to update standard crm fileds, rememember that they will not be saved if they are read-only.
Let me make myself clear with an example.
I was developing an HTML web resource and I was changing the value of a field only shown on the footer. In order to be able to change the value of that field I had to place it on the form as well and hide it. This means that I set, from the form designer, the following properties:
So what you should do is: Hide the field on the form but don't set the field to be read-only as shown below.
If it is a bug or it is normal, I don't know. If someone knows the reason of this behavior please let me know.
Thank you.
Let me make myself clear with an example.
I was developing an HTML web resource and I was changing the value of a field only shown on the footer. In order to be able to change the value of that field I had to place it on the form as well and hide it. This means that I set, from the form designer, the following properties:
- Visible by default = false
- Field is read-only = true
So what you should do is: Hide the field on the form but don't set the field to be read-only as shown below.
If it is a bug or it is normal, I don't know. If someone knows the reason of this behavior please let me know.
Thank you.
19 March 2012
How to pass parameters from subgrids to JavaScript
In this post I will show you how to pass parameters regarding a grid or subgrid to a JavaScript function by clicking a custom button on the ribbon menu.
A common requirement is to pass poarameters to a JavaScript function called when clicking a Ribbon button.
To pass parameters regarding a grid or a subgrid we can use the "crmparameter" element of the RibbonDiffXml file.
The available parameters can be devided in three groups:
<CommandDefinition Id="Mscrm.SubGrid.opportunity.Command">
<EnableRules>
<EnableRule Id="Mscrm.SubGrid.opportunity.EnableRule"></EnableRule>
</EnableRules>
<DisplayRules></DisplayRules>
<Actions>
<JavaScriptFunction Library="$webresource:functions.js" FunctionName="MyFunction" >
<CrmParameter Value="SelectedControlSelectedItemIds" />
<CrmParameter Value="SelectedControlSelectedItemCount" />
</JavaScriptFunction>
</Actions>
</CommandDefinition>
To access these parameters add them to your function definition like below:
function MyFunction(SelectedControlSelectedItemIds, SelectedControlSelectedItemCount) {
for (i = 0; i < SelectedControlSelectedItemCount; i++) {
}
}
So as you can see it is possible to access records displayed in a subgrid using supported code, so don't get tempted to use document.getElementById. ;)
I hope this help.
A common requirement is to pass poarameters to a JavaScript function called when clicking a Ribbon button.
To pass parameters regarding a grid or a subgrid we can use the "crmparameter" element of the RibbonDiffXml file.
The available parameters can be devided in three groups:
- Selected items
- SelectedControlSelectedItemCount: The number of selected items in a grid or subgrid.
- SelectedControlSelectedItemIds: A string array of GUID Id values for all selected items in a grid.
- SelectedControlSelectedItemReferences: An array of EntityReference objects that represent all the selected items in the grid
- All items
- SelectedControlAllItemCount: The number of items in a grid or subgrid.
- SelectedControlAllItemIds: A string array of GUID Id values for all items in a grid.
- SelectedControlAllItemReferences: An array of EntityReference objects that represent all the items in the grid.
- Unselected items
- SelectedControlUnselectedItemCount: The number of unselected items in a grid or subgrid.
- SelectedControlUnselectedItemIds: A string array of GUID Id values for all unselected items in a grid.
- SelectedControlUnselectedItemReferences: An array of EntityReference objects that represent all the unselected items in the grid.
<CommandDefinition Id="Mscrm.SubGrid.opportunity.Command">
<EnableRules>
<EnableRule Id="Mscrm.SubGrid.opportunity.EnableRule"></EnableRule>
</EnableRules>
<DisplayRules></DisplayRules>
<Actions>
<JavaScriptFunction Library="$webresource:functions.js" FunctionName="MyFunction" >
<CrmParameter Value="SelectedControlSelectedItemIds" />
<CrmParameter Value="SelectedControlSelectedItemCount" />
</JavaScriptFunction>
</Actions>
</CommandDefinition>
To access these parameters add them to your function definition like below:
function MyFunction(SelectedControlSelectedItemIds, SelectedControlSelectedItemCount) {
for (i = 0; i < SelectedControlSelectedItemCount; i++) {
}
}
So as you can see it is possible to access records displayed in a subgrid using supported code, so don't get tempted to use document.getElementById. ;)
I hope this help.
14 March 2012
ITSM SEMINAR
Using an ITIL accredited ITSM suite can dramatically improve your operations by:
•Enabling users to log and track their own incidents, change requests and service requests
•Automating SLA adherence, workflows and assignments
•Root cause analysis with deep insight from powerful reporting tools
•Increase end user satisfaction with timely actions and accurate information
During this seminar you will learn how to achieve all of the above and more. Our ITSM enables you to leverage your exisiting environment and maximise effectiveness. We look forward to seeing you in two weeks!!
Register here: http://www.alfapeople.com/UK/EN/Events/Pages/Events.aspx
•Enabling users to log and track their own incidents, change requests and service requests
•Automating SLA adherence, workflows and assignments
•Root cause analysis with deep insight from powerful reporting tools
•Increase end user satisfaction with timely actions and accurate information
During this seminar you will learn how to achieve all of the above and more. Our ITSM enables you to leverage your exisiting environment and maximise effectiveness. We look forward to seeing you in two weeks!!
Register here: http://www.alfapeople.com/UK/EN/Events/Pages/Events.aspx
03 March 2012
Crm 2011 Survey Creator Beta
The Crm 2011 Survey Creator is a CRM solution that allows us to create Surveys in CRM 2011.
A survey can be created in different ways, for example:
2. Add Questions Groups to the Survey
We can specify an order number, this will tell in what order to display the question group in the Survey.
3. Add Questions to the Group/s
4. Here we can specify the question name, if an answer is required or not, the order in which the Question will appear in the Question Group in the Survey, and the type of answer we expect:
Fill the required fields (the Performed On date will automatically filled in and is read-only) and then save the Survey, when the survey form reloads it will come up with the questions on the form, see below.
Here we can see Text fields, Ye/No and Dropdown.
Here we can see a required field in red and Number fields. We also have a little help that says what is the type of the filed. However, Validation on the fields is implemented on the Survey Form. See below for the validation messages:
The date separator is based on your CRM customisation so if you enter an "invalid" date you'll be prompted with an error.
Finally if we try to save a Survey without filling require field we will not be able to save the survey and the user will be prompted with the above message.
One more thing, the survey can be marked as complete by clicking the Custom button "Save as Complete". If we do this the next time we open the survey, all the fields will be disabled and the Survey status at the bottom will say "Completed".
This is all for today, hope you find this post useful.
A survey can be created in different ways, for example:
- From the Contact form
- From the Account form
- From the Main Page Navigation Pane
- Survey Template
- Questions Group
- Question
- Survey
1. Create a Survey Template
2. Add Questions Groups to the Survey
We can specify an order number, this will tell in what order to display the question group in the Survey.
3. Add Questions to the Group/s
4. Here we can specify the question name, if an answer is required or not, the order in which the Question will appear in the Question Group in the Survey, and the type of answer we expect:
- Text
- Number
- Decimal
- Date
- Yes or No
- Picklist/Dropdown
Fill the required fields (the Performed On date will automatically filled in and is read-only) and then save the Survey, when the survey form reloads it will come up with the questions on the form, see below.
![]() |
| Part 1 |
Here we can see Text fields, Ye/No and Dropdown.
![]() |
| Part 2 |
Here we can see a required field in red and Number fields. We also have a little help that says what is the type of the filed. However, Validation on the fields is implemented on the Survey Form. See below for the validation messages:
![]() | |||
| Validation on Number Fields |
![]() | |||
| Validation on Date Fields |
The date separator is based on your CRM customisation so if you enter an "invalid" date you'll be prompted with an error.
![]() | |
| Validation on Decimal Fields |
![]() |
| Validation on Required Fields |
Finally if we try to save a Survey without filling require field we will not be able to save the survey and the user will be prompted with the above message.
One more thing, the survey can be marked as complete by clicking the Custom button "Save as Complete". If we do this the next time we open the survey, all the fields will be disabled and the Survey status at the bottom will say "Completed".
![]() |
| Survey Complete |
This is all for today, hope you find this post useful.
11 January 2012
How to retrieve all the activities for a record
In this post I will show how to retrieve all the activities in which a record is related to as:
Here is the query:
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">
<entity name="activitypointer">
<attribute name="activitytypecode" />
<attribute name="subject" />
<attribute name="statecode" />
<attribute name="prioritycode" />
<attribute name="modifiedon" />
<attribute name="activityid" />
<attribute name="instancetypecode" />
<order attribute="modifiedon" descending="false" />
<link-entity name="activityparty" from="activityid" to="activityid" alias="aa">
<filter type="and">
<condition attribute="participationtypemask" operator="in">
<value>4</value>
<value>3</value>
<value>11</value>
<value>6</value>
<value>7</value>
<value>9</value>
<value>8</value>
<value>5</value>
<value>10</value>
<value>1</value>
<value>2</value>
</condition>
<condition attribute="partyid" operator="eq" uitype="contact" value="Xrm.Page.data.entity.getId()" />
</filter>
</link-entity>
</entity>
</fetch>
The corresponding advanced find query is:
Hope this helps,
Luciano.
- BBC Recipient
- CC Recipient
- Customer Optional Attendee
- Organizer
- Owner
- Regarding
- Required attendee
- Resource
- Sender
- To Recipient
Here is the query:
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">
<entity name="activitypointer">
<attribute name="activitytypecode" />
<attribute name="subject" />
<attribute name="statecode" />
<attribute name="prioritycode" />
<attribute name="modifiedon" />
<attribute name="activityid" />
<attribute name="instancetypecode" />
<order attribute="modifiedon" descending="false" />
<link-entity name="activityparty" from="activityid" to="activityid" alias="aa">
<filter type="and">
<condition attribute="participationtypemask" operator="in">
<value>4</value>
<value>3</value>
<value>11</value>
<value>6</value>
<value>7</value>
<value>9</value>
<value>8</value>
<value>5</value>
<value>10</value>
<value>1</value>
<value>2</value>
</condition>
<condition attribute="partyid" operator="eq" uitype="contact" value="Xrm.Page.data.entity.getId()" />
</filter>
</link-entity>
</entity>
</fetch>
The corresponding advanced find query is:
Hope this helps,
Luciano.
20 October 2011
13 September 2011
Interact with CRM form objects from Silverlight through the HTML Bridge
In this post I will show you how we can access attributes on the form via Silverlight managed code.
When you host a Silverlight application in a HTML page you can access the HTML Document Object Model (DOM) from the managed code, and call managed code from JavaScript. This is possible using the HTML Bridge technology available in Silverlight.
In the same way you can access the xrm objects on a Dynamics Crm form using managed code and vice versa when a Silverlight application is embedded in a Crm form.
In this post I will show you a simple example of how to do this.
Recipe for this example:
After having created the Silverlight application let's add the following code to the Button_Click event handler:
add this a reference to the "System.Windows.Browser" to your Silverlight project and the using statement in order to be able to use the HTML Bridge,
using System.Windows.Browser;
private void Button_Click(object sender, RoutedEventArgs e)
{
//Get the xrm object using the dynamic type
dynamic xrm = (ScriptObject)HtmlPage.Window.GetProperty("Xrm");
//this is the number of employees attribute
var numEmployees = xrm.Page.data.entity.attributes.get("numberofemployees");
try
{
if (!string.IsNullOrEmpty(InputText.Text))
{
//convert to int the input from SL text box
var employees = Convert.ToInt32(InputText.Text);
//set the attribute on the Crm form
numEmployees.setValue(employees);
}
else
{
//set the attribute on the form
numEmployees.setValue(null);
}
}
catch(System.FormatException fe)
{
HtmlPage.Window.Alert(string.Format("{0}", fe.Message));
}
catch(System.OverflowException ofe)
{
HtmlPage.Window.Alert(string.Format("{0}" , ofe.Message));
}
}
After having added the Silverlight web resource (see this post) we can now embed it in a form (the account form in this example), thje result is shown below:
To test the application enter a value into the textbox and enter "Go", you should see the number of employee populated with the value you have inserted in alternative you can leave the text box empty.
If you try to enter a value that is too large, too small or is not a number an exception will be thrown and a message will be displayed (see below) . Note that the messages are once again shown using the HTML Bridge through the following lines of code:
HtmlPage.Window.Alert(string.Format("{0}", fe.Message));
HtmlPage.Window.Alert(string.Format("{0}" , ofe.Message));
Alternatively we could have used a Silverlight Child window or simple MessageBox.Show("")
Well that's all really! Next time we will see how to call a Silverlight method from Crm form.
Stay tuned.
Luciano.
When you host a Silverlight application in a HTML page you can access the HTML Document Object Model (DOM) from the managed code, and call managed code from JavaScript. This is possible using the HTML Bridge technology available in Silverlight.
In the same way you can access the xrm objects on a Dynamics Crm form using managed code and vice versa when a Silverlight application is embedded in a Crm form.
In this post I will show you a simple example of how to do this.
Recipe for this example:
- I will create a simple Silverlight application
- Create a Silverlight web resource
- Add the web resource to a Crm form
- Verify
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Stretch">
<TextBox x:Name="InputText" Width="200" Height="23"/>
<Button Content="Go" Width="22" Height="20" Click="Button_Click" Margin="4,0,0,0"/>
</StackPanel>
<TextBox x:Name="InputText" Width="200" Height="23"/>
<Button Content="Go" Width="22" Height="20" Click="Button_Click" Margin="4,0,0,0"/>
</StackPanel>
After having created the Silverlight application let's add the following code to the Button_Click event handler:
add this a reference to the "System.Windows.Browser" to your Silverlight project and the using statement in order to be able to use the HTML Bridge,
using System.Windows.Browser;
private void Button_Click(object sender, RoutedEventArgs e)
{
//Get the xrm object using the dynamic type
dynamic xrm = (ScriptObject)HtmlPage.Window.GetProperty("Xrm");
//this is the number of employees attribute
var numEmployees = xrm.Page.data.entity.attributes.get("numberofemployees");
try
{
if (!string.IsNullOrEmpty(InputText.Text))
{
//convert to int the input from SL text box
var employees = Convert.ToInt32(InputText.Text);
//set the attribute on the Crm form
numEmployees.setValue(employees);
}
else
{
//set the attribute on the form
numEmployees.setValue(null);
}
}
catch(System.FormatException fe)
{
HtmlPage.Window.Alert(string.Format("{0}", fe.Message));
}
catch(System.OverflowException ofe)
{
HtmlPage.Window.Alert(string.Format("{0}" , ofe.Message));
}
}
After having added the Silverlight web resource (see this post) we can now embed it in a form (the account form in this example), thje result is shown below:
To test the application enter a value into the textbox and enter "Go", you should see the number of employee populated with the value you have inserted in alternative you can leave the text box empty.
If you try to enter a value that is too large, too small or is not a number an exception will be thrown and a message will be displayed (see below) . Note that the messages are once again shown using the HTML Bridge through the following lines of code:
HtmlPage.Window.Alert(string.Format("{0}", fe.Message));
HtmlPage.Window.Alert(string.Format("{0}" , ofe.Message));
Alternatively we could have used a Silverlight Child window or simple MessageBox.Show("")
Well that's all really! Next time we will see how to call a Silverlight method from Crm form.
Stay tuned.
Luciano.
02 September 2011
Silverlight 5 RC Available for download
Silverlight 5 RC Available for download on: http://www.silverlight.net/
Subscribe to:
Posts (Atom)














