Generate and download file from Word dialog - ms-word

Is there a way to generate a file with some JSON content and prompt the user with a saveAs dialog.
This is from an open dialog in word.
The object could be like (will be quite a lot bigger in practice)
var obj = {a: 1, b: 2, c: 'qwerty'}
I tried to uri encode and using window.open without any luck.
content = JSON.stringify(obj);
uriContent = "data:application/octet-stream," + encodeURIComponent(content);
newWindow = window.open(uriContent, 'filename');

I did this for XML, but it requires some back-end and front-end work. On the backend, you need some ASPX like this:
string filename = "export.xml";
byte[] data = Convert.FromBase64String(Request.QueryString[0].Replace("\"", ""));
string decodedString = System.Text.Encoding.UTF8.GetString(data);
// set the http content type to "APPLICATION/OCTET-STREAM
Response.ContentType = "APPLICATION/OCTET-STREAM";
System.String disHeader = "Attachment; Filename=\"" + filename + "\"";
Response.AppendHeader("Content-Disposition", disHeader);
Response.Flush();
Response.Write(decodedString);
I called mine "download.aspx." Then on the front-end, you have to use AJAX. This created a form region on the page and the forces a submit of the form to start the download:
// Helper function to load a form and then send the post results to a
// new window so that we can get the download button
function ajax_download(url, data, input_name) {
try {
$('#form-div').append("<form method='GET' action='" +
url + "' target='_blank'>" +
"<input type=hidden name='" + input_name + "' value='" +
JSON.stringify(data) + "'/></form>");
$('#form-div').find('form').submit();
} catch (err) {
showNotification("error", err.description);
}
}
To call it, you simply make this this call from your JavaScript code where you want it:
xml = "<xml><data>12345</data></xml>";
ajax_download('./Download.aspx', btoa(xml), 'xml');
In this case, mine is targeting XML and always creates a file called "export.xml", but you can adjust it as needed.

Related

Dynamics CRM 365 : Downloading a Word Document Template via a Button on the Ribbon

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?
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" />
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:
Create new process of type Action, bound to entity that you want to
create a template for (the reason why I suggest Action here, is that
it can be easily invoked using JavaScript and CRM WebAPI)
In this Action add single step - invoke an Action and choose
built-in action "SetWordTemplate"
Set Properties of this action - choose the template that you need
and dynamically set the target to current entity (using Dynamic
Values assistant) If you never used this action - it simply creates
a given word template and adds it as an annotation to your entity
Now you need to write logic inside your button (I'm assuming you
know how to add a button using Ribbon Workbench or whatever)
Call your action using WebAPI
Find annotation that was just created for your entity with the
attached document
Download the attachment (you can show some prompt for the user or
simply force the download the file, user will have to save it)
Delete the annotation
Maybe not a one-liner, but keeps you in the supported zone...
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");
}
}
Just to simplify #TeamEASI.com answer a little here is what I did.
Add a button to the ribbon using XRMToolBox Ribbon Workbench 2016.
Create a JS web resource like the one bellow.
/*
* 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
Create a command using XRMToolBox Ribbon Workbench 2016 with the following parameters to execute the JS when the button is clicked.
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");
}
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

Is it possible to send page content via email using UI5?

My application is designed to create a table which is later edited by the user. After this I need my application to send the page content via email.
I used URLHelper's trigger email() but through this I am able to trigger the email with to, cc, subject, text body but my ui5 application is not able to insert the table into the email.
Can someone please suggest something? or is it even possible?
I won't mind using plain javascript either, Point is I need to do this without using the backend.
We do something similar on one of our apps. I added a button to the screen which when clicked invokes a 'mailto', and populates the email client with the to, subject and body. The body is created as part of the script. We basically read the table contents into an array, then loop through the entries using a forEach. Keep in mind using mailto or even the URLHelper does not allow you to use HTML formatted text in the 'body' of the email. So, if you're looking for something pretty, you may be out of luck.
onNotifyUserPress: function(oEvent) {
var oItem = oEvent.getSource();
var oBinding = oItem.getBindingContext();
// Set some vars for the email package
var sEmpEmail = oBinding.getProperty("Smtp");
var sEmpName = oBinding.getProperty("STEXT_2");
var sEmailSubject = "Your Subject " + sEmpName;
// Create DateFormat Object
var oDateFormat = DateFormat.getDateTimeInstance({pattern: "dd/MM/yyyy"});
// Retrieve Table Data
var oTable = this.getView().byId("yourTable");
var aTableData = oTable.getBinding("items").getContexts();
// Build the email body
var sBody = sEmpName + " - Some Body Text\n\n";
sBody += "Field 1 | " + "Field 2 | " + "Field 3 | " + "Field 4" + "\n";
// Loop through table data and build the output for the rest of the email body
aTableData.forEach(function(oModel) {
var oModelData = oModel.getObject();
var sEndDate = oDateFormat.format(oModelData.Vendd);
var sStatus = this._formatStatus(oModelData.ZQ_STAT);
sBody += (oModelData.Essential === "X" ? "Yes" : "No") + " | " + oModelData.Ttext + " | " + sEndDate + " | " + sStatus + "\n";
}.bind(this));
// Open email client window and prepopulate with info
window.open("mailto:" + sEmpEmail + "&subject=" + sEmailSubject + "&body=" + encodeURIComponent(sBody), "_self");
},
You'll obviously need to update the code to point to your table data. In this particular instance, we have an object page with a couple of sections. Each section contains a table which loads a list of entities that are associated with the user. As the data is already loaded and exists in the model, this may not work in the same fashion as what you're trying to do (if I understand correctly), as you need to send an email after the data is entered/modified?
Hopefully this can at least get you started!
Cheers!

Calculating an oauth signature

I am trying something a little specific, namely trying to call a REST API. I have been following these instructions.
I have been very careful to ensure that I am creating the "Signature base string" correctly. They define it to be created like this:
(HTTP Method)&(Request URL)&(Normalized Parameters)
You can double check if need be in my code, but I am very sure that it is fine.
The problem that I am having is creating what they call the "oauth signature" and mine isn't matching theirs. They it should be created like this:
Use the HMAC-SHA1 signature algorithm as defined by the [RFC2104] to sign the request where text is the Signature Base String and key is the concatenated values of the Consumer Secret and Access Secret separated by an '&' character (show '&' even if Access Secret is empty as some methods do not require an Access Token).
The calculated digest octet string, first base64-encoded per [RFC2045], then escaped using the [RFC3986] percent-encoding (%xx) mechanism is the oauth_signature.
I express this in my code like so:
var oauthSignature = CryptoJS.HmacSHA1(signatureBaseString, sharedSecret+"&");
var oauthSignature64 = encodeURIComponent(CryptoJS.enc.Base64.stringify(oauthSignature));
console.log("hash in 64: " + oauthSignature64);
I am using Google's CryptoJS library. I take the signature base string as the text, I then take my consumer secret as the key concatenated with "&", I have no Access key and it isn't required but that is OK. I then base 64 encode the result of that hash, after which I URI encode it, please could some guys sanity check my understanding of that and my usage/expressing of it in code using this library, I think this is where my problem is.
Here is my full code:
var fatSecretRestUrl = "http://platform.fatsecret.com/rest/server.api";
var d = new Date();
var sharedSecret = "xxxx";
var consumerKey = "xxxx";
//this is yet another test tyring to make this thing work
var baseUrl = "http://platform.fatsecret.com/rest/server.api?";
var parameters = "method=food.search&oauth_consumer_key="+consumerKey+"&oauth_nonce=123&oauth_signature_method=HMAC-SHA1&oauth_timestamp="+getTimeInSeconds()+"&oauth_version=1.0&search_expression=banana";
var signatureBaseString = "POST&" + encodeURIComponent(baseUrl) + "&" + encodeURIComponent(parameters);
console.log("signature base string: " + signatureBaseString);
var oauthSignature = CryptoJS.HmacSHA1(signatureBaseString, sharedSecret+"&");
var oauthSignature64 = encodeURIComponent(CryptoJS.enc.Base64.stringify(oauthSignature));
console.log("hash in 64: " + oauthSignature64);
var testUrl = baseUrl+"method=food.search&oauth_consumer_key=xxxx&oauth_nonce=123&oauth_signature="+oauthSignature64+"&oauth_signature_method=HMAC-SHA1&oauth_timestamp="+getTimeInSeconds()+"&oauth_version=1.0&search_expression=banana";
console.log("final URL: " + testUrl);
var request = $http({
method :"POST",
url: testUrl
});
I have taken care to ensure that the parameters that I am posting are in lexicographical order and I am very sure that it is correct.
The response that I am getting back is:
Invalid signature: oauth_signature 'RWeFME4w2Obzn2x50xsXujAs1yI='
So clearly either
I haven't understood the instructions provided in the API
Or I have understood them but I haven't expressed them in that way in my code
Or both of the above
Or I have made some subtle mistake somewhere that I can't see
I would really appreciate a sanity check, this has taken a while.
well...I did it, but not the way that I thought I would end up doing it, I spent hours trying it out with angular then JQuery, then finally I tried Node JS and it worked, here are two working examples, one with food.get and another with foods.search
food.get example
var rest = require('restler'),
crypto = require('crypto'),
apiKey = 'xxxx',
fatSecretRestUrl = 'http://platform.fatsecret.com/rest/server.api',
sharedSecret = 'xxxx',
date = new Date;
// keys in lexicographical order
var reqObj = {
food_id: '2395843', // test query
method: 'food.get',
oauth_consumer_key: apiKey,
oauth_nonce: Math.random().toString(36).replace(/[^a-z]/, '').substr(2),
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: Math.floor(date.getTime() / 1000),
oauth_version: '1.0'
};
// make the string...got tired of writing that long thing
var paramsStr = '';
for (var i in reqObj) {
paramsStr += "&" + i + "=" + reqObj[i];
}
// had an extra '&' at the front
paramsStr = paramsStr.substr(1);
var sigBaseStr = "GET&"
+ encodeURIComponent(fatSecretRestUrl)
+ "&"
+ encodeURIComponent(paramsStr);
// no access token but we still have to append '&' according to the instructions
sharedSecret += "&";
var hashedBaseStr = crypto.createHmac('sha1', sharedSecret).update(sigBaseStr).digest('base64');
// Add oauth_signature to the request object
reqObj.oauth_signature = hashedBaseStr;
rest.get(fatSecretRestUrl, {
data: reqObj,
}).on('complete', function(data, response) {
console.log(response);
console.log("DATA: " + data + "\n");
});
foods.search example
var rest = require('restler'),
crypto = require('crypto'),
apiKey = 'xxxx',
fatSecretRestUrl = 'http://platform.fatsecret.com/rest/server.api',
sharedSecret = 'xxxx',
date = new Date;
// keys in lexicographical order
var reqObj = {
method: 'foods.search',
oauth_consumer_key: apiKey,
oauth_nonce: Math.random().toString(36).replace(/[^a-z]/, '').substr(2),
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: Math.floor(date.getTime() / 1000),
oauth_version: '1.0',
search_expression: 'mcdonalds' // test query
};
// make the string...got tired of writing that long thing
var paramsStr = '';
for (var i in reqObj) {
paramsStr += "&" + i + "=" + reqObj[i];
}
// had an extra '&' at the front
paramsStr = paramsStr.substr(1);
var sigBaseStr = "POST&"
+ encodeURIComponent(fatSecretRestUrl)
+ "&"
+ encodeURIComponent(paramsStr);
// again there is no need for an access token, but we need an '&' according to the instructions
sharedSecret += "&";
var hashedBaseStr = crypto.createHmac('sha1', sharedSecret).update(sigBaseStr).digest('base64');
// Add oauth_signature to the request object
reqObj.oauth_signature = hashedBaseStr;
rest.post(fatSecretRestUrl, {
data: reqObj,
}).on('complete', function(data, response) {
console.log(response);
console.log("DATA: " + data + "\n");
});
really sorry to anyone using Angular or JQuery, if I ever have a spare minute or two I will try it with angular, also anyone using angular if you get CORS related errors just start chrome like this:
chromium-browser --disable-web-security - I do this on terminal
or add that extension to some chrome shortcut on windows, just as a quick work around, hope it helps anybody out there.

How to Send automatic emails like newsletter

I see some question closed by administrator with similar question.
I will try to explain in detail the solution I'm looking for:
First I'm trying to develop a windows console application.
Sending frequency: monday to friday
How many users: from 5 to 20
Format of data:
A table where rows are countries and columns different types of products and the cells represent the sale of that country-product
Limitation:
Service users prefer not to use a pdf or excel or attachment of any type should be a html format possible to show in the body of mail
Currently I am creating the report almost artisan with something like:
.....
var myStringBuilder = new StringBuilder("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">");
myStringBuilder.Append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">");
myStringBuilder.Append("</head><body>");
myStringBuilder.AppendLine();
myStringBuilder.Append("<table style=\"font-family: Calibri; text-align: right; font-size: x-small;\">");
myStringBuilder.AppendLine();
var header = new StringBuilder("<tr><th>a</th><th>CATEG1</th><th>CATEG2</th><th>CATEG3</th><th>CATEG4</th><th>TOTAL</th></tr>");
var line = new StringBuilder("<tr><td>a</td><td>b</td><td>c</td><td>z</td><td>e</td><td>f</td></tr>");
foreach (var a in _actuals)
{
line.Replace("a", a.CountryName);
if(a.Segment.ToLowerInvariant().Equals("categ1")) {
line.Replace("b", "[" + string.Format("{0:0,#}", a.LsvLcFact / 1000) + "]"); }
else if (a.Segment.ToLowerInvariant().Equals("categ2")) {
line.Replace("c", "[" + string.Format("{0:0,#}", a.LsvLcFact / 1000) + "]"); }
else if (a.Segment.ToLowerInvariant().Equals("categ3")) {
line.Replace("z", "[" + string.Format("{0:0,#}", a.LsvLcFact / 1000) + "]"); }
else {
line.Replace("e", "[" + string.Format("{0:0,#}", a.LsvLcFact / 1000) + "]"); }
}
.....
And in the class to send mail something like:
public void SendResumen(string subject ,StringBuilder content)
{
var oMsg = new MailMessage...;
... add users
oMsg.Subject = subject;
var mimeType = new ContentType("text/html");
var alternate = AlternateView.CreateAlternateViewFromString(content.ToString(), mimeType);
oMsg.AlternateViews.Add(alternate);
oMsg.IsBodyHtml = true;
var cli = Client();
cli.Send(oMsg);
}
Finally, I hope you understand what I'm doing and the question is:
Is there a tool I can use to not generate as crafted the message body?
I have a VB application that can send out messages that have an HTML payload, and it doesn't use the AlternateView functionality.
Dim mail As New MailMessage(fromUser.Email, toUser.Email)
mail.Subject = subject
mail.IsBodyHtml = True
mail.Body = message
While it would be nice to use the AlternateView functionality for those email clients that can't read the HTML payload, your code can't really use it because you're only supplying an HTML message. If you did manage to get the AlternateView functionality to work here, any client that couldn't read HTML would see the HTML markup instead of just simple message text. For this to truly work, I think you would need to provide both a text and HTML email message body.
I Accept the suggest from #Jim-Mischel

Unable to add picture with url parameters on Facebook feed dialog (Direct URL)

I was wondering how could I add parameters to my picture URl on this example:
https://www.facebook.com/dialog/feed?app_id=...&link=...&picture=www.blablabladotcom?parameter1=1&parameter2=2&name=...&caption=...&description=...&redirect_uri=...
I tried with %26 encoding and picture doesn't show up.
What is strange is that when I try %26 on the redirect_uri parameter, it works fine.
Any tips about this one??
You need to encode all the parts properly (example Java Script code below):
var _FBAPPID = "xxxxxxxxxx", // your app ID (mandatory)
_name = "name",
_text = "text",
_link = "link",
_picture = "http://cdn2.insidermonkey.com/blog/wp-content/uploads/2012/10/facebook4-e1349213205149.jpg", // some google image - replace it
_caption = "caption",
_redirect_uri = "http://google.com" // this URL must be from within the domain you specified in your app's settings
var _params = "&name=" + encodeURIComponent(_name)
+ "&description=" + encodeURIComponent(_text)
+ "&link=" + encodeURIComponent(_link)
+ "&picture=" + encodeURIComponent(_picture)
+ "&caption=" + encodeURIComponent(_caption)
+ "&redirect_uri=" + encodeURIComponent(_redirect_uri);
var _href = "http://www.facebook.com/dialog/feed?app_id=" + _FBAPPID + _params + "&display=touch";
I have also added display=touch because I'm using direct URL only for mobile devices.
Source is here.