Bookmarklet: Copy and Paste Lookup

Lookups (including partylist and customer) cannot be copy pasted. I have encountered couple of scenarios where I needed this capability. I have developed these bookmarklets to solve this issue.

Copy Lookup

Select and the drag the code below to the bookmark bar.

javascript:(function(){let contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){let Xrm=contentPanels[0].contentWindow.Xrm;let currentControl=Xrm.Page.ui.getCurrentControl();if(currentControl&¤tControl.getControlType()==='lookup'){let currentLookup=currentControl.getAttribute().getValue();if(currentLookup){let serialisedLookupValue=JSON.stringify(currentLookup.map(x=>{let c={};({id:c.id,name:c.name,type:c.type,typename:c.typename,entityType:c.entityType}=x);return c;}));sessionStorage.setItem('ryr_serialisedLookup',serialisedLookupValue);alert('Lookup copied. Ready to paste');}}else{alert('No field has been selected or the currently selected field is not a lookup');}}else{alert('Entity form not detected');}})();void 0;

Source:

(function () {
	let contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		let Xrm = contentPanels[0].contentWindow.Xrm;
		let currentControl = Xrm.Page.ui.getCurrentControl();
		if (currentControl && currentControl.getControlType() === 'lookup') {
			let currentLookup = currentControl.getAttribute().getValue();
			if (currentLookup) {
				let serialisedLookupValue = JSON.stringify(
						currentLookup.map(x =  > {
								let c = {};
								({
									id : c.id,
									name : c.name,
									type : c.type,
									typename : c.typename,
									entityType : c.entityType
								}
										 = x);
								return c;
							}));
				sessionStorage.setItem('ryr_serialisedLookup', serialisedLookupValue);
				alert('Lookup copied. Ready to paste');
			}
		} else {
			alert('No field has been selected or the currently selected field is not a lookup');
		}
	} else {
		alert('Entity form not detected');
	}
})();

Paste Lookup

Select and the drag the code below to the bookmark bar.

javascript:(function(){let contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){let Xrm=contentPanels[0].contentWindow.Xrm;let currentControl=Xrm.Page.ui.getCurrentControl();if(currentControl&¤tControl.getControlType()==='lookup'){let currentLookup=currentControl.getAttribute();let copiedLookupValue=sessionStorage.getItem('ryr_serialisedLookup');if(copiedLookupValue){currentLookup.setValue(JSON.parse(copiedLookupValue));} else{alert('Please select a lookup to copy first before pasting');}}else{alert('No field has been selected or the currently selected field is not a lookup');}}else{alert('Entity form not detected');}})();void 0;

Source:

(function () {
	let contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		let Xrm = contentPanels[0].contentWindow.Xrm;
		let currentControl = Xrm.Page.ui.getCurrentControl();
		if (currentControl && currentControl.getControlType() === 'lookup') {
			let currentLookup = currentControl.getAttribute();
			let copiedLookupValue = sessionStorage.getItem('ryr_serialisedLookup');
			if(copiedLookupValue){
				currentLookup.setValue(JSON.parse(copiedLookupValue));
			}
			else{
				alert('Please select a lookup to copy first before pasting');
			}
		} else {
			alert('No field has been selected or the currently selected field is not a lookup');
		}
	} else {
		alert('Entity form not detected');
	}
})();

Instructions

  1. Select the lookup field to be copied. The lookup will usually be highlighted with a blue background when it is selectedlookup-selected
  2. Run the Copy Lookup bookmarklet. You will get a alert message confirming that the lookup has been copied
  3. Select the lookup field that is the target of the paste. The lookup has to be of the same type i.e. if the copied lookup was a contact lookup, the target lookup also has to be a contact lookup
  4. Run the Paste Lookup bookmarklet
  5. Save the record

I have tested these bookmarklets only in Chrome 53 and Firefox 50.

Advertisements

Convert Personal View to System View

When you design a query from the “Advanced Find” window, you can save the query for future use. I call this “Personal View”, but the official name for this is “Saved View”. This view is only visible to the person who saved the view (unless it is shared/reassigned).

As more personal views get created, it becomes an issue during migration, as these are not transported in the solution xml. You can of course use Solution Extender to copy this across, but I would like to do this right from the Advanced Find window. Apart from data migration, another scenario where you might find the need to create a system view from a personal view, is when you are constantly sharing views to a large number of people of group. When you are doing this, it is good time to actually make this a system view.

With these scenarios in mind, I have developed a solution that simplifies the process of creating a system view from a personal view. After installing the managed solution, you will see a new button called “Promote to System View” in the advanced find, when you switch to the “Saved Views” tab.

screenshot

To create a system view from the personal view, simply choose the views that need to be converted, and click the “Promote to System View” button. A message will be displayed after the conversion is complete. Once you refresh the window, you should be able to see the newly created system view(s).

Please log any issues/feedback/feature request in the github repo -> https://github.com/rajyraman/Personal-View-to-System-View/issues

You can download the managed solution from https://github.com/rajyraman/Personal-View-to-System-View/releases

Restricting the customer lookup

EDIT (04/07/2017): After testing this in 8.2.1, I have simplified the code as getLookupTypes and setLookupTypes can be used straight from the lookup itself, instead of going through getLookupDataAttribute.

First off, I have tested this only in CRMOnline on 8.1.0 and 8.2.1 so this might not work for you if you are are in older version. You can now easily restrict the default entity of the lookup, if it links to multiple types. e.g. customer lookup. The script below is for attribute of type “customer”, but you can follow a similar approach for “partylist” as well.

These new functions are undocumented, so they are technically unsupported. So, use this at your own risk. In the script below, I am restricting a field with schemaname “customerid” and of type “customer” to “contact” entity only.

JavaScript

var lookup = Xrm.Page.getAttribute('customerid');

//check if multiple type dropdowns enabled for this lookup and it is not a partylist. For partylist we might want to select an account and a contact
if (lookup.getLookupTypes().length > 1
     && !lookup.getIsPartyList()) {
    lookup.setLookupTypes(['contact']);
}

Screenshots

LookupLookup Search Customer

You can use this script during form load and easily restrict the lookup type.

Bookmarklet: Display Optionset values

Unless you have generated strongly typed classes, looking up Optionset values for the selected value or any other value, is one of the most annoying things to do as a developer. You can use the bookmarklet below to display Optionset values, along with the Optionset text for all the Optionsets on a form. You can drag the minified source to your bookmark bar, for creating the bookmarklet.

This is how a sample contact form looks after executing the bookmarklet.

Altered Optionset

Unminified

(function () {
	var contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		var Xrm = contentPanels[0].contentWindow.Xrm;
		var frameDocument = contentPanels[0].contentWindow.document;
		Xrm.Page.ui.controls.forEach(function (c) {
			if (c.getControlType() !== 'optionset')
				return;
			var attribute = c.getAttribute();
			var selectedOptionValue = attribute.getValue();
			var options = attribute.getOptions();
			var isClearOptions = options.some(function (o) {
					return o.text.indexOf(' (') === -1;
				});
			if (isClearOptions) {
				c.clearOptions();
			}
			options.forEach(function (o) {
				if (o.text && o.text.indexOf(' (') === -1) {
					o.text = o.text + ' (' + o.value + ')';
				}
				c.addOption(o);
			});
			if (selectedOptionValue && isClearOptions) {
				attribute.setValue(selectedOptionValue);
			}
		});
	} else {
		alert('Entity form not detected');
	}
})();

Minified

javascript:(function(){var contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){var Xrm=contentPanels[0].contentWindow.Xrm;var frameDocument=contentPanels[0].contentWindow.document;Xrm.Page.ui.controls.forEach(function(c){if(c.getControlType()!=='optionset')
return;var attribute=c.getAttribute();var selectedOptionValue=attribute.getValue();var options=attribute.getOptions();var isClearOptions=options.some(function(o){return o.text.indexOf(' (')===-1;});if(isClearOptions){c.clearOptions();}
options.forEach(function(o){if(o.text&&o.text.indexOf(' (')===-1){o.text=o.text+' ('+o.value+')';}
c.addOption(o);});if(selectedOptionValue&&isClearOptions){attribute.setValue(selectedOptionValue);}});}else{alert('Entity form not detected');}})();void 0;

Bookmarklet: Customise Form & Refresh Subgrids

If you just want to quickly customise the form layout use this bookmarklet below. Don’t add any new fields from this window, as it will create the field with the Default Publisher’s prefix, which by default is “new_”.

Unminified

var entityName = prompt("Entity?", "");
var url = Xrm.Page.context.getClientUrl() + '/main.aspx?etn=' + entityName + '&extraqs=formtype%3dmain&pagetype=formeditor';
window.open(url, '_blank');

Minified

javascript:var%20entityName=prompt("Entity?","");var%20url=Xrm.Page.context.getClientUrl()+'/main.aspx?etn='+entityName+'&extraqs=formtype%3dmain&pagetype=formeditor';window.open(url,'_blank');void%200;

If you just want to quickly refresh all the subgrids in the form, without refreshing the whole page, use this bookmarklet.

Unminified

(function () {
	var contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		var Xrm = contentPanels[0].contentWindow.Xrm;
		Xrm.Page.ui.controls.forEach(function (c) {
			if (c.getControlType() === 'subgrid') {
				c.refresh();
			}
		});
	} else {
		alert('Entity form not detected');
	}
})();

Minified

javascript:(function(){var%20contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return%20d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){var%20Xrm=contentPanels[0].contentWindow.Xrm;Xrm.Page.ui.controls.forEach(function(c){if(c.getControlType()==='subgrid'){c.refresh();}});}else{alert('Entity%20form%20not%20detected');}})();void%200;

Advanced Find and “In” condition

Advanced Find, in its current form, doesn’t have the capability to do a true “In” condition for text, datetime and numbers. For example this is how you would really do an “In” condition for a text field.

In condition

However the above condition would not return any results, as Advanced Find doesn’t allow separating valid values with a semi-colon. Query like the one above, has to be actually be done like the screenshot below.

In condition Actual

This is actually clunky and involves too many clicks. The query capability in Advanced Find also differs to what the workflow condition offers. For e.g. you can do a true “In” condition in workflow editor. The workflow condition builder uses a similar UI to Advanced Find, but it also displays a “In” condition for text attribute, which is not displayed in the Advanced Find.

Workflow In Condition

Workflow Step

Bookmarklet Solution

In order to do a “in” condition, where the target values are separated by semi-colons, you can use the bookmarklet below.

Unminified code

var contentPanel = $('#crmContentPanel > iframe');
if (contentPanel && contentPanel.length > 0) {
	var targetFrame = contentPanel[0].contentWindow;
	var isFetchModified = false;
	targetFrame.ExecuteQuery();
	var xml = targetFrame.$get("FetchXml").value;
	var xmlDoc = $.parseXML(xml),
	$xml = $(xmlDoc);
	$xml.find('condition[value]').each(function (i, d) {
		var inCondition = '';
		var multipleValues = $(d).attr('value');
		if (multipleValues.indexOf(';') === -1)
			return;
		multipleValues.split(';').forEach(function (c) {
			inCondition += ('<value>' + c + '</value>');
		});
		$(d).attr('operator', 'in');
		$(d).removeAttr('value');
		$(d).html(inCondition);
		isFetchModified = true;
	});
	if (isFetchModified) {
		targetFrame.$get("FetchXml", $get("resultRender")).value = '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">' + xmlDoc.documentElement.innerHTML + '</fetch>';
	}
	targetFrame.changeArea(targetFrame.ResultsPage);
	targetFrame.$get('resultRender').submit();
} else {
	alert('Cannot locate Advanced Find Frame');
}
void 0;

Minified code for bookmark bar

javascript:var contentPanel=$('#crmContentPanel > iframe');if(contentPanel&&contentPanel.length>0){var targetFrame=contentPanel[0].contentWindow;var isFetchModified=false;targetFrame.ExecuteQuery();var xml=targetFrame.$get("FetchXml").value;var xmlDoc=$.parseXML(xml),$xml=$(xmlDoc);$xml.find('condition[value]').each(function(i,d){var inCondition='';var multipleValues=$(d).attr('value');if(multipleValues.indexOf(';')===-1)
return;multipleValues.split(';').forEach(function(c){inCondition+=('<value>'+c+'</value>');});$(d).attr('operator','in');$(d).removeAttr('value');$(d).html(inCondition);isFetchModified=true;});if(isFetchModified){targetFrame.$get("FetchXml",$get("resultRender")).value='<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">'+xmlDoc.documentElement.innerHTML+'</fetch>';}
targetFrame.changeArea(targetFrame.ResultsPage);targetFrame.$get('resultRender').submit();}else{alert('Cannot locate Advanced Find Frame');}
void 0;

You basically continue to use the “equal” condition and then separate the valid values with semi-colon. Run the bookmarklet after that and the “equal” condition will be converted into a “in”. This currently works only for text fields. Datetime and number fields have additional format validations, hence it is not possible to use “;” to separate out the values.

I have tested this bookmarklet in CRM Online and CRM2015 with Firefox and Chrome.

Known Issues

A javascript error pops up before the results are displayed in Firefox. This can be ignored as the correct results are shown.

Bookmarklet: Turn off autosave & lookup on newwindow

The first bookmarklet turns off autosave for the current record, and refreshes the form without saving. Drag the below to your favorites bar.

javascript:(function(){var%20contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return%20d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){var%20Xrm=contentPanels[0].contentWindow.Xrm;Xrm.Page.data.refresh(false).then(function(){Xrm.Page.data.entity.addOnSave(function(econtext){var%20eventArgs=econtext.getEventArgs();if(eventArgs.getSaveMode()===70||eventArgs.getSaveMode()===2){eventArgs.preventDefault();}});alert('Form%20refreshed%20without%20save.%20Autosave%20turned%20off.');},function(errorCode,message){alert(message);});}else{alert('Entity%20form%20not%20detected');}})();void%200;

Below the unminified source.

(function () {
	var contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		var Xrm = contentPanels[0].contentWindow.Xrm;
		Xrm.Page.data.refresh(false).then(function () {
			Xrm.Page.data.entity.addOnSave(function (econtext) {
				var eventArgs = econtext.getEventArgs();
				if (eventArgs.getSaveMode() === 70 || eventArgs.getSaveMode() === 2) {
					eventArgs.preventDefault();
				}
			});
			alert('Form refreshed without save. Autosave turned off.');
		}, function (errorCode, message) {
			alert(message);
		});
	} else {
		alert('Entity form not detected');
	}
})();

The second bookmarklet open the selected lookup in a new window. Starting from CRM2015 every link you click inside a record form, opens in the same window. This can be annoying sometimes. To use this bookmarklet, you’ll just have to select the lookup on the record and execute the bookmarklet. This will open the lookup record in a new window. Below is the bookmarklet.

javascript:(function(){var%20contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return%20d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){var%20Xrm=contentPanels[0].contentWindow.Xrm;var%20currentControl=Xrm.Page.ui.getCurrentControl();if(currentControl.getControlType()==='lookup'){var%20currentLookup=currentControl.getAttribute().getValue();if(currentLookup){var%20entityName=currentLookup[0].type,entityId=currentLookup[0].id;var%20url=Xrm.Page.context.getClientUrl()+'/main.aspx?etc='+entityName+'&id='+entityId+'&newWindow=true&pagetype=entityrecord';window.open(url,'_blank');}}else{alert('The%20currently%20selected%20control%20is%20not%20a%20lookup');}}else{alert('Entity%20form%20not%20detected');}})();void%200;

This is the unminified source.

(function () {
	var contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		var Xrm = contentPanels[0].contentWindow.Xrm;
		var currentControl = Xrm.Page.ui.getCurrentControl();
		if (currentControl.getControlType() === 'lookup') {
			var currentLookup = currentControl.getAttribute().getValue();
			if (currentLookup) {
				var entityName = currentLookup[0].type,
				entityId = currentLookup[0].id;
				var url = Xrm.Page.context.getClientUrl() + '/main.aspx?etc=' + entityName + '&id=' + entityId + '&newWindow=true&pagetype=entityrecord';
				window.open(url, '_blank');
			}
		} else {
			alert('The currently selected control is not a lookup');
		}
	} else {
		alert('Entity form not detected');
	}
})();

References:

https://msdn.microsoft.com/en-us/library/gg509060.aspx