Reputation: 1021
Currently users have to click the ellipses, word templates, and finally quote to download the word template.
To make it easier for our users we would like to have the document download when pressing the "print quote" button on the ribbon.
Is this possible? If so how would I go about doing this? I understand how to edit the ribbon using the ribbon workbench. I need to know how to download a word template using the ribbon.
If the solution is using the ribbon workbench, what command can I enter to get the word template to download?
Upvotes: 7
Views: 11652
Reputation: 1
For everyone who wants to generate word documents from JS in Dyn365 Online;
In the internet you can find 'list of undocumented SDK messages' for Dynamics (CLICK). It is unsupported, but it works now.
because of their internal nature, they can become obsolete in future releases of CRM without any notice from Microsoft
Thanks to that I prepare a sample code for downloading Word templates (you only need to put that in function, change parameters like EntityTypeCode, templateid, filename... etc.)
fetch(Xrm.Utility.getGlobalContext().getClientUrl() + "/api/data/v9.0/ExportWordDocument", {
method: "POST",
body: JSON.stringify({
EntityTypeCode: 1084, //change to valid code
SelectedRecords: "[\"{6F7651A2-1AE4-ED11-A7C7-000D3ABE768E}\"]", //array of records
SelectedTemplate: {
'@odata.type': "Microsoft.Dynamics.CRM.documenttemplate",
documenttemplateid: "296cb76d-0b59-e811-8126-5065f38bd3c1" //change to valie templateId or retrieve id by template name earlier
}
}),
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"Accept-Language": "*"
}
}).then((response) => response.json())
.then((json) => {
const binaryString = window.atob(json.WordFile);
const bytes = new Uint8Array(binaryString.length);
const arrayBuffer = bytes.map((byte, i) => binaryString.charCodeAt(i));
const blob = new Blob([arrayBuffer]);
const fileName = 'TEST.docx';
if (navigator.msSaveBlob) {
// IE 10+
navigator.msSaveBlob(blob, fileName);
} else {
const link = document.createElement('a');
// Browsers that support HTML5 download attribute
if (link.download !== undefined) {
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', fileName);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}
);
Upvotes: 0
Reputation: 161
With the new version of CRM this javascript code, need to be amened to remove the unsupported API, as well some addtional changes to be able to work also with CHROME.
below my working version,
/*
* Author: Matthew Hunt
* Changes: Philippe Guarino
* File: vsi_DownloadTemplate.js
* Date: 22/09/2021
* Project: CRM USA
* Description: DownloadTemplate() allows the user to download a document template
* via a button on the ribbon.
*
* @param entitytypecode: the type code of the entity. In the ribbon workbench set a
* CRM parameter with value PrimaryEntityTypeCode. ex: 1063
*
* @param templateid: the id for the template you want to download. I had to go to
* the database to find this and pass it as a string parameter in the ribbon workbench.
* For example:
* SELECT DocumentTemplateId, Name FROM dbo.DocumentTemplateBase WHERE Name Like '%Quote%';
* returns something like 4AB391A4-D247-E711-80D3-005056914EA2
* Unforunatly, anytime the template is updated, you'll probably have to get the new id.
*
* @param templatetype: the code for the template type. Pass this value in the ribbon
* workbench as a int param. ex: 9940 is a documenttemplate
*
* @param filename: the resulting name of the file that will be downloaded to the users
* computer. Pass this value in the ribbon workbench as a string param. ex: Quote.docx
*
*/
function DownloadTemplate(entitytypecode, templateid, templatetype, filename, formContext)
{
// var formContext = executionContext.getFormContext(); // get formContext
// retrieve the entity id from the current page
var entityid = new Array();
entityid.push(formContext.data.entity.getId());
// try and make a request for the document template
try
{
// clear the page of any previous errors
formContext.ui.clearFormNotification("docerror");
// the path that will be used to retrieve the word template
var globalContext = Xrm.Utility.getGlobalContext();
var funcpath = globalContext.getClientUrl() + "/_grid/print/print_data.aspx";;
// open the request to create the template
var req = new XMLHttpRequest();
req.open("POST", funcpath, true);
req.responseType = "arraybuffer";
req.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
req.setRequestHeader("Accept-Language", "en-US,en;q=0.8");
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// on completion, run the bellow function
req.onreadystatechange = function ()
{
// request complete
if (this.readyState == 4)
{
req.onreadystatechange = null;
// check if we got back a 200 from the request
if (this.status >= 200 && this.status <= 299)
{
// add the download url to an a tag and then click the a tag
// to download the document
var mimetype = (2 === 2) ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var blob = new Blob([req.response],
{
type: mimetype
});
var dloadurl = (window.URL ? URL : webkitURL).createObjectURL(blob);
var a = document.createElement("a");
// if ie, because ie sucks
if (navigator.msSaveOrOpenBlob)
{
navigator.msSaveOrOpenBlob(blob, filename);
// else a browser that doesn't suck
}
else
{
document.body.appendChild(a);
a.style = "display: none";
a.href = dloadurl;
a.download = filename;
a.click();
URL.revokeObjectURL(dloadurl);
}
}
}
};
// compile the data to send with the request
var formdata = "exportType=MergeWordTemplate&selectedRecords=" + encodeURIComponent(JSON.stringify(entityid)) +
"&associatedentitytypecode=" + entitytypecode + "&TemplateId=" + templateid + "&templatetype=" + templatetype;
// make the request to create the template
req.send(formdata);
}
catch (err)
{
PrintError(err.message);
}
}
/*
* PrintError() is a helper method to display any errors to the user.
*/
function PrintError(msg)
{
Xrm.Page.ui.setFormNotification("An Error occurred generating the word document, please contact support if the issue persists. " + msg, "ERROR", "docerror");
}
Upvotes: 0
Reputation: 1
To get the entity code, run below query:
SELECT coalesce(OriginalLocalizedName,name) AS DisplayName, Name AS SchemaName, ObjectTypeCode
FROM EntityLogicalView
ORDER BY ObjectTypeCode
and for Template ID
:
SELECT DocumentTemplateId, Name FROM dbo.DocumentTemplateBase
Upvotes: -1
Reputation: 1021
Just to simplify @TeamEASI.com answer a little here is what I did.
/*
* Author: Matthew Hunt
* File: vsi_DownloadTemplate.js
* Date: 12/20/2017
* Project: CRM USA
* Description: DownloadTemplate() allows the user to download a document template
* via a button on the ribbon.
*
* @param entitytypecode: the type code of the entity. In the ribbon workbench set a
* CRM parameter with value PrimaryEntityTypeCode. ex: 1063
*
* @param templateid: the id for the template you want to download. I had to go to
* the database to find this and pass it as a string parameter in the ribbon workbench.
* For example:
* SELECT DocumentTemplateId, Name FROM dbo.DocumentTemplateBase WHERE Name Like '%Quote%';
* returns something like 4AB391A4-D247-E711-80D3-005056914EA2
* Unforunatly, anytime the template is updated, you'll probably have to get the new id.
*
* @param templatetype: the code for the template type. Pass this value in the ribbon
* workbench as a int param. ex: 9940 is a documenttemplate
*
* @param filename: the resulting name of the file that will be downloaded to the users
* computer. Pass this value in the ribbon workbench as a string param. ex: Quote.docx
*
*/
function DownloadTemplate(entitytypecode, templateid, templatetype, filename){
// retrieve the entity id from the current page
var entityid = new Array();
entityid.push(Xrm.Page.data.entity.getId());
// try and make a request for the document template
try{
// clear the page of any previous errors
Xrm.Page.ui.clearFormNotification("docerror");
// the path that will be used to retrieve the word template
var funcpath = Xrm.Page.context.getClientUrl() + "/_grid/print/print_data.aspx";
// open the request to create the template
var req = new XMLHttpRequest();
req.open("POST", funcpath, true);
req.responseType = "arraybuffer";
req.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
req.setRequestHeader("Accept-Language", "en-US,en;q=0.8");
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// on completion, run the bellow function
req.onreadystatechange = function () {
// request complete
if (this.readyState == 4) {
req.onreadystatechange = null;
// check if we got back a 200 from the request
if (this.status >= 200 && this.status <= 299) {
// add the download url to an a tag and then click the a tag
// to download the document
var mimetype = (2 === 2) ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var blob = new Blob([req.response], { type: mimetype });
var dloadurl = URL.createObjectURL(blob);
var a = document.createElement("a");
// if ie, because ie sucks
if (navigator.msSaveOrOpenBlob) {
navigator.msSaveOrOpenBlob(blob, filename);
// else a browser that doesn't suck
} else {
document.body.appendChild(a);
a.style = "display: none";
a.href = dloadurl;
a.download = filename;
a.click();
URL.revokeObjectURL(dloadurl);
}
}
};
// compile the data to send with the request
var formdata = "exportType=MergeWordTemplate&selectedRecords=" + encodeURIComponent(JSON.stringify(entityid)) +
"&associatedentitytypecode=" + entitytypecode + "&TemplateId=" + templateid + "&templatetype=" + templatetype;
// make the request to create the template
req.send(formdata);
}catch (err) {
PrintError(err.message);
}
}
/*
* PrintError() is a helper method to display any errors to the user.
*/
function PrintError(msg){
Xrm.Page.ui.setFormNotification("An Error occurred generating the word document, please contact support if the issue persists. " + msg, "ERROR", "docerror");
}
IE fix: .click() giving access denied in IE11
Upvotes: 1
Reputation: 86
ExecuteWordMerge = function (wordtemplateid, entitytypecodeint, ids, templatetype, fieldforfilename, filenameoverride) {
try {
Xrm.Page.ui.clearFormNotification("worderror");
var funcpath = Xrm.Page.context.getClientUrl() + "/_grid/print/print_data.aspx";
if (typeof ids !== "object") {
var tids = ids;
ids = new Array();
ids.push(tids);
}
var wordTemplateId = wordtemplateid;//"f1f7b994-543b-e711-8106-c4346bac2908" test data;
var currentEntityTypeCode = entitytypecodeint;//"10063" test data;
var templateType = (templatetype || 9940); //9940 is global and 9941 is personal
var fieldForFileName = (fieldforfilename || "");
var formdata = "exportType=MergeWordTemplate&selectedRecords=" + encodeURIComponent(JSON.stringify(ids)) +
"&associatedentitytypecode=" + currentEntityTypeCode + "&TemplateId=" + wordTemplateId + "&TemplateType=" + templateType;
var req = new XMLHttpRequest();
req.open("POST", funcpath, true);
req.responseType = "arraybuffer";
req.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
req.setRequestHeader("Accept-Language", "en-US,en;q=0.8");
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.onreadystatechange = function () {
if (this.readyState == 4) {/* complete */
req.onreadystatechange = null;
if (this.status >= 200 && this.status <= 299) {//200 range okay
var mimetype = (2 === 2) ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var blob = new Blob([req.response], { type: mimetype });
var fileNameTemplate = req.getResponseHeader('content-disposition').split('filename=')[1].replace(/'/g, "");
var dloadurl = URL.createObjectURL(blob);
var filename = (fieldForFileName !== "" && Xrm.Page.getAttribute(fieldForFileName) !== null && Xrm.Page.getAttribute(fieldForFileName).getValue() !== "") ?
Xrm.Page.getAttribute(fieldForFileName).getValue() : fileNameTemplate;
filename = filenameoverride || filename;
//new code, prevent IE errors
if (navigator.msSaveOrOpenBlob) {
navigator.msSaveOrOpenBlob(blob, filename);
return;
}
else if (window.navigator.msSaveBlob) { // for IE browser
window.navigator.msSaveBlob(blob, filename);
return;
}
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = dloadurl;
a.download = filename;
a.click();
URL.revokeObjectURL(dloadurl);
//window.location = dloadurl;//we can use just this instead of creating an anchor but we don't get to the name the file
}
else {
Xrm.Page.ui.setFormNotification("An Error occurred generating the word document, please contact support if the issue persists,code: " + this.status, "ERROR", "worderror");
}
}
};
req.send(formdata);
}
catch (err) {
Xrm.Page.ui.setFormNotification("An Error occurred generating the word document, please contact support if the issue persists. " + err.message, "ERROR", "worderror");
}
}
Upvotes: 1
Reputation: 3586
It's possible to do this using only supported features of CRM (of course I'm sure it's also possible to do using unsupported javascript, but I don't have time currently to investigate this). The steps that you should take to achieve the functionality you want:
Maybe not a one-liner, but keeps you in the supported zone...
Upvotes: 3
Reputation: 23310
When you click the templates flyout, it's dynamically populated through an invocation of /AppWebServices/DocumentTemplate.asmx
, which returns the XML for the menu.
The flyout for Word Templates in the Incident home page grid looks like this:
<Menu Id="incident|NoRelationship|HomePageGrid|Mscrm.HomepageGrid.incident.WordTemplates.Menu">
<MenuSection Id="incident|NoRelationship|HomePageGrid|Mscrm.HomepageGrid.incident.WordTemplates.Menu.CreateTemplates" Title="Create Word Template" Sequence="10" DisplayMode="Menu16">
<Controls Id="incident|NoRelationship|HomePageGrid|Mscrm.HomepageGrid.incident.WordTemplates.Menu.CreateTemplates.Controls">
<Button Id="incident|NoRelationship|HomePageGrid|Mscrm.HomepageGrid.incident.WordTemplates.Menu.CreateTemplates.Controls.00000000-0000-0000-0000-000000000000" Command="incident|NoRelationship|HomePageGrid|Mscrm.WordTemplate.CreateWordTemplate.Grid" Sequence="10" ToolTipDescription="Create Word Template" Alt="Create Word Template" LabelText="Create Word Template" />
</Controls>
</MenuSection>
<MenuSection Id="incident|NoRelationship|HomePageGrid|Mscrm.HomepageGrid.incident.WordTemplates.Menu.WordTemplates" Title="Word Templates" Sequence="20" DisplayMode="Menu16">
<Controls Id="incident|NoRelationship|HomePageGrid|Mscrm.HomepageGrid.incident.WordTemplates.Menu.WordTemplates.Controls">
<Button Id="incident|NoRelationship|HomePageGrid|Mscrm.HomepageGrid.incident.WordTemplates.Menu.WordTemplates.Controls.9b77c5b0-1033-4741-a01c-afdbdb1c3f22" Command="incident|NoRelationship|HomePageGrid|Mscrm.WordTemplate.TemplatesMenu.Grid" Sequence="10" ToolTipDescription="Case Summary" Alt="Case Summary" LabelText="Case Summary" />
</Controls>
</MenuSection>
</Menu>
I don't have the means to try it out at the moment, but I'd try and "copy" the last <Button>
:
<Button Id="incident|NoRelationship|HomePageGrid|Mscrm.HomepageGrid.incident.WordTemplates.Menu.WordTemplates.Controls.9b77c5b0-1033-4741-a01c-afdbdb1c3f22" Command="incident|NoRelationship|HomePageGrid|Mscrm.WordTemplate.TemplatesMenu.Grid" Sequence="10" ToolTipDescription="Case Summary" Alt="Case Summary" LabelText="Case Summary" />
Upvotes: 4