When we first create new field names in crm we try to name them so they make sense to our users, but as time goes on a number of fields get classified by other names. This makes it hard to get the field names on forms without opening the form and finding it there. On larger forms this is a time consuming way to get the fieldname. Adding a ribbon button with some custom js fixes this. The js grabs all attributes on the form and replaces their label with the schemaname.
function SetAllLabelsToSchema() {
try {
var attributes = Xrm.Page.data.entity.attributes.get();
for (var ix = 0; ix < attributes.length; ix++) {
var attribute = attributes[ix];
var FieldName = attribute.getName();
if (Xrm.Page.getControl(FieldName) !== null) {
Xrm.Page.getControl(FieldName).setLabel(FieldName);
}
}
} catch (ex) {
//this is a non critical failure.
}
}
Another must have is a way to get the current records Id. In 2011 this was still easy as the Id was in the url. With the later updates you need to send a link in an email to steal the guid. The below js uses some DOM commands to copy the current record guid to the clipboard for quick use in development or queries.
function copyToClipboard(text) {
if (window.clipboardData && window.clipboardData.setData) {
// IE specific code path to prevent textarea being shown while dialog is visible.
return clipboardData.setData("Text", text);
} else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
var textarea = document.createElement("textarea");
textarea.textContent = text;
textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in MS Edge.
document.body.appendChild(textarea);
textarea.select();
try {
return document.execCommand("copy"); // Security exception may be thrown by some browsers.
} catch (ex) {
console.warn("Copy to clipboard failed.", ex);
return false;
} finally {
document.body.removeChild(textarea);
}
}
}
Friday, September 22, 2017
Monday, September 18, 2017
Dynamics CRM Audit Query
Due to a large number of records in our system we find that many times trying to open an audit log from a record results in a sql timeout error in CRM. Below I finally spent some time in coming up with a SQL query to get similar data. Replace the @objectid value with the record that you are attempting to view the audit for and you should get a returned table with the specified data.
NOTE: dates/times in the database are stored UTC, so some additional care should be taken with any dates returned in below query.
--drop table #audit
declare @objectid uniqueidentifier = '{AA2B1B39-02E7-E611-80C5-0050569E2CDA}'
select auditid, useridname, createdon, operation, AttributeMask, '~' + ChangeData + '~' ChangeData, objectid
into #audit
from audit a
where a.objectid = @objectid
declare @objecttypecode int = (select top 1 objecttypecode from audit where objectid = @objectid)
;with #tmp(auditid, useridname, createdon, operation, Attribute, AttributeMask, ChangeDataStr, ChangeData) as (
select auditid, useridname, createdon, operation,
LEFT(AttributeMask, CHARINDEX(',',AttributeMask+',')-1), STUFF(AttributeMask, 1, CHARINDEX(',',AttributeMask+','), ''),
LEFT(ChangeData, CHARINDEX('~',ChangeData+'~')-1), STUFF(ChangeData, 1, CHARINDEX('~',ChangeData+'~'), '')
from #audit
union all
select auditid, useridname, createdon, operation,
LEFT(AttributeMask, CHARINDEX(',',AttributeMask+',')-1), STUFF(AttributeMask, 1, CHARINDEX(',',AttributeMask+','), ''),
LEFT(ChangeData, CHARINDEX('~',ChangeData+'~')-1), STUFF(ChangeData, 1, CHARINDEX('~',ChangeData+'~'), '')
from #tmp
where ChangeData > ''
)
select auditid, useridname, createdon, operation, a.TableColumnName, ChangeDataStr
from #tmp t
join attribute a on t.attribute = a.ColumnNumber
join entity e on a.entityid = e.entityid and e.ObjectTypeCode = @objecttypecode
order by createdon desc
OPTION (maxrecursion 0)
-- normally recursion is limited to 100. If you know you have very long
-- strings, uncomment the option
NOTE: dates/times in the database are stored UTC, so some additional care should be taken with any dates returned in below query.
--drop table #audit
declare @objectid uniqueidentifier = '{AA2B1B39-02E7-E611-80C5-0050569E2CDA}'
select auditid, useridname, createdon, operation, AttributeMask, '~' + ChangeData + '~' ChangeData, objectid
into #audit
from audit a
where a.objectid = @objectid
declare @objecttypecode int = (select top 1 objecttypecode from audit where objectid = @objectid)
;with #tmp(auditid, useridname, createdon, operation, Attribute, AttributeMask, ChangeDataStr, ChangeData) as (
select auditid, useridname, createdon, operation,
LEFT(AttributeMask, CHARINDEX(',',AttributeMask+',')-1), STUFF(AttributeMask, 1, CHARINDEX(',',AttributeMask+','), ''),
LEFT(ChangeData, CHARINDEX('~',ChangeData+'~')-1), STUFF(ChangeData, 1, CHARINDEX('~',ChangeData+'~'), '')
from #audit
union all
select auditid, useridname, createdon, operation,
LEFT(AttributeMask, CHARINDEX(',',AttributeMask+',')-1), STUFF(AttributeMask, 1, CHARINDEX(',',AttributeMask+','), ''),
LEFT(ChangeData, CHARINDEX('~',ChangeData+'~')-1), STUFF(ChangeData, 1, CHARINDEX('~',ChangeData+'~'), '')
from #tmp
where ChangeData > ''
)
select auditid, useridname, createdon, operation, a.TableColumnName, ChangeDataStr
from #tmp t
join attribute a on t.attribute = a.ColumnNumber
join entity e on a.entityid = e.entityid and e.ObjectTypeCode = @objecttypecode
order by createdon desc
OPTION (maxrecursion 0)
-- normally recursion is limited to 100. If you know you have very long
-- strings, uncomment the option
Thursday, August 31, 2017
First look at CRM Editable Grids Post 1
Editable grids are this wonderful new feature in Dynamics 365, but for many of us longtime users with a lot of js security and business logic some additional work is needed before they are useful. I will be posting a few of the tricks I learn as I dive into incorporating them.
This first post will deal with limiting which fields a user can edit as many of the form security applied previously does not apply to Editable Grids. What do I mean by this? First off read only attributes applied to fields on a form do not honor this setting in editable grids. As such neither does calls to setDisabled in form js. Also as editable grids are set at an entity level, users are able to create their own views and add any column, even those not on any form, to those views and gain editable rights to them. Supposedly field level security is upheld on editable grids, but that does little to help if you don't want to go through the thousands of fields you have created and decide who has access to edit them.
The first step I took is to disable all fields in the selected row. I will not go into how to setup editable grids or wire up events as this is all documented nicely many places, but where I ran into trouble is what the js should look like to accomplish this goal.
var editableFields = ["emailaddress1", "telephone1"];
function Grid_And_Form(executionContext) {
var entityObject = executionContext.getFormContext().data.entity;
var attributes= entityObject.attributes.getAll();
for (var i = 0; i< attributes.length; i++) {
if (editableFields.indexOf(attributes[i].getName()) == -1) {
console.log(attributes[i].getName());
var attrControl= attributes[i].controls.get(0);
attrControl.setDisabled(true);
}
}
}
By passing executionContext to the js, and retrieving the entity we can get all attributes. From there getting the first control for each attribute (there should only ever be one, as you cannot add a field to a view multiple times) we are able to disable that field. Creating a list up front of fields we want to allow to be editable allows us to easily modify the list, and document which fields are available.
Subscribe to:
Posts (Atom)