How get attachments in outlook plugin? - outlook

Hi how can i get attachments and send it to my java server?
in docs its say:
var item = Office.context.mailbox.item;
var options = {asyncContext: {currentItem: item}};
item.getAttachmentsAsync(options, callback);
function callback(result) {
if (result.value.length > 0) {
for (i = 0 ; i < result.value.length ; i++) {
result.asyncContext.currentItem.getAttachmentContentAsync(result.value[i].id, handleAttachmentsCallback);
}
}
}
function handleAttachmentsCallback(result) {
// Parse string to be a url, an .eml file, a base64-encoded string, or an .icalendar file.
switch (result.value.format) {
case Office.MailboxEnums.AttachmentContentFormat.Base64:
// Handle file attachment.
break;
case Office.MailboxEnums.AttachmentContentFormat.Eml:
// Handle email item attachment.
break;
case Office.MailboxEnums.AttachmentContentFormat.ICalendar:
// Handle .icalender attachment.
break;
case Office.MailboxEnums.AttachmentContentFormat.Url:
// Handle cloud attachment.
break;
default:
// Handle attachment formats that are not supported.
}
}
But i have several errors witch this example.
first is item.getAttachmentsAsync is not a function
then im tried to use
result.asyncContext.currentItem.getAttachmentContentAsync(item.attachments[2].id, handleAttachmentsCallback);
but its never called calback
How can i get attachments and send them by XMLHttpRequest to my server?

Most likely what is happening is that you are trying this code on a read item. The getAttachmentsAsync fn only exists in compose mode, so you would see the error above if you are not composing an email. For read emails you should be able to just access the attachments property Office.context.mailbox.item.attachments (https://learn.microsoft.com/en-us/javascript/api/outlook/office.attachmentdetails?view=outlook-js-preview)

Read Mode:
https://learn.microsoft.com/en-us/office/dev/add-ins/outlook/get-attachments-of-an-outlook-item?view=outlook-js-preview
Compose Mode:
https://learn.microsoft.com/en-us/office/dev/add-ins/outlook/add-and-remove-attachments-to-an-item-in-a-compose-form?view=outlook-js-preview
However, as mentioned in Jadams answer, getAttachmentContentAsync IS supported in Read Mode, and you can get that to the the Base64 encoding of attachments. (the first link will be updated soon to reflect this)

Related

getAttachmentContentAsync does not return anything

I've read the documentation over on: https://learn.microsoft.com/en-us/javascript/api/outlook/office.messagecompose?view=outlook-js-preview&preserve-view=true#getComposeTypeAsync_options__callback_ , but nothing has been returned. Not even error messages. It doesn't return anything at all. The code i've used is below:
var item = Office.context.mailbox.item;
var options = {asyncContext: {currentItem: item}};
item.getAttachmentsAsync(options, callback);
function callback(result) {
if (result.value.length > 0) {
for (i = 0 ; i < result.value.length ; i++) {
result.asyncContext.currentItem.getAttachmentContentAsync(result.value[i].id, handleAttachmentsCallback);
}
}
}
function handleAttachmentsCallback(result) {
// Parse string to be a url, an .eml file, a base64-encoded string, or an .icalendar file.
switch (result.value.format) {
case Office.MailboxEnums.AttachmentContentFormat.Base64:
// Handle file attachment.
console.log(result.value.content);
break;
case Office.MailboxEnums.AttachmentContentFormat.Eml:
// Handle email item attachment.
console.log("Attachment is a message.");
break;
case Office.MailboxEnums.AttachmentContentFormat.ICalendar:
// Handle .icalender attachment.
console.log("Attachment is a calendar item.");
break;
case Office.MailboxEnums.AttachmentContentFormat.Url:
// Handle cloud attachment.
console.log("Attachment is a cloud attachment.");
break;
default:
// Handle attachment formats that are not supported.
}
}
I have inserted the code for the MessageCompose form and I know that it requires the v1.8 API. I have tested other methods such as getAttachmentsAsync(options, callback) and they work as expected.
As a test, I have been using console logs at specific points where the code works up to. The console outputs the result of callback(result) as OSF.DDA.AsyncResult where status=succeeded. However, it displays "This value was evaluated upon first expanding. It may have changed since then." and it won't allow me to inspect the Object when you click expand.
Additionally logging result.value[i] in callback works as well, with the same issue where I cant expand the object for more details.
Why won't getAttachmentContentAsync return anything?

outlook add-in image & files

I try to find solution to my problems but didn't find any where,hope that someone here can save me.
I write add-in in JavaScript on VS2015 that encrypte and decrypte body messages.
1. The first problem is with images that the receiver can't see .
(Talk about images that copy into the body by "insert picture inline" button)
In Compose mode we encrypte the message and then when we decrypte it's works good because the compose mode is a client side and he his recognize the local images .
In read mode when user want to decrypte the message and to see the images he couldn't see because the encrypte prevent outlook to convert the local image to data on the server .
In my code I take the body message like this ( compose mode )
item.body.getAsync(
item.body.getAsync(
"html",
{ asyncContext: "This is passed to the callback" },
function callback(resultbody) {
......Here we send the body for ENCRYPT.
}))
then , the user send the encrypte message by clicking 'send' regular.
In the read mode I just print it to my html to check if the decrypte is good :
(JSON.parse(xhr.responseText).Data.Content));
and then i get icon of picture ,but not success to show the real pic .
The src of the icon is going for place that not access for him ..
<img src="https://attachment.outlook.office.net/owa/*****/service.svc/s/GetFileAttachment?id=AAMkADUwMDE0YWM1LTYwODctNG ......
How can i take this tag of image and do something that the receiver can see the image ? I don't want that user will be need to upload image to body from my my add-in instead of the original outlook. I try to convert the image to base-64 string, but with what I have in the tag it not enough ,just with original picture and also it success to show in html but not in the body of message with SetAsync function..
2. The second problem is with attachments .
I upload files with dropzone plug-in (because outlook don't give access to take attachment and change him). So, after I upload files and encrypte him I make some new file with the response from server with File API of JS :
ar f = new File([""], "filename.txt", {type: "text/plain", lastModified: date}) . .. .
than I want to attach the file to mail, so the only method that do this is:
addFileAttachmentAsync(uri, attachmentName, optionsopt, callback opt)
then,I need to create a url for file for this method so I use this method:
var objectURL = URL.createObjectURL(f);
But now when I use the method addFileAttachmentAsync with objectURL it's write that there is a problem and its can't attach it , I think that the URL is incorrect .
Thanks all!!
For everyone who look any solution to this problems..
**In outlook web this solutions works good but in Outlook Desktop there is a problem of synchronize with server so there is a delay with saveAsync function without any solution to this right now , so it's work but need to wait a little bit.You could read more about it here.
First Question:
There is a problem in outlook add-in with when using getAsync and then setAsync functions . The problem occurs when there is some image inside the body . It's happen because when you take the body in Html format and then return the body with some different the image still not 'upload' and the src is being wrong .
I success to workaround this problem using Outlook rest API.
So the workaround is going like this:
Get the body message in type of Html by getAsync method. create div
element and set the return body message inside the div.
To get message id, you need to save your message as a draft with saveAsync function.
To make request to Outlook rest
API you need to get access token , so call to getCallbackTokenAsync function and save the access
token.
Make Http Request to outlook rest API to get all attachment exist in
the message.
Find the right ID of your image and replace the image src to the
base-64 of the image that you get from your request to outlook rest
API.
Finally , you could set your new body with SetAsync function .
Code:
item.body.getAsync(
Office.CoercionType.Html,
{ asyncContext: "This is passed to the callback" },
function callback(resultbody) {
var bodyDiv = document.createElement('div');
bodyDiv.innerHTML = content;
Office.context.mailbox.item.saveAsync(
function callback(result) {
var myNewItemSaved = result.value;
Office.context.mailbox.getCallbackTokenAsync({ isRest: true },
function (result) {
if (result.status === "succeeded") {
var accessToken = result.value;
var itemId = "";
if (Office.context.mailbox.diagnostics.hostName === 'OutlookIOS')
itemId = Office.context.mailbox.item.itemId;
else
itemId = Office.context.mailbox.convertToRestId(myNewItemSaved,
Office.MailboxEnums.RestVersion.v2_0);
var xhr3 = new XMLHttpRequest();
xhr3.open("GET", "https://outlook.office.com/api/v2.0/me/messages/" + itemId + "/attachments", true);
xhr3.setRequestHeader("Content-type", "application/json");
xhr3.setRequestHeader("Access-Control-Allow-Origin", "*");
xhr3.setRequestHeader("Authorization", "Bearer " + accessToken);
xhr3.send();
xhr3.onreadystatechange = function () {
if (xhr3.readyState == 4) {
if (xhr3.status == 200) {
var allImages = JSON.parse(xhr3.response).value;
var isDesktop = false;
var imgSrcId = bodyDiv.getElementsByTagName('img')[0].getAttribute("src");
if (imgSrcId.indexOf("cid") != -1) //Outlook Desktop
isDesktop = true;
for (var i = 0; i < allImages.length; i++) {
if (bodyDiv.getElementsByTagName('img')[i].getAttribute("src").indexOf("base64")!=-1)
continue;
if (isDesktop)
imgSrcId = bodyDiv.getElementsByTagName('img')[i].getAttribute("src");
else
imgSrcId = bodyDiv.getElementsByTagName('img'[i].getAttribute("originalsrc");
imgSrcId = imgSrcId.substr(4, imgSrcId.length);
var wantedImg;
for (var j = 0; j < allImages.length; j++) {
if ((allImages[j].ContentId).localeCompare(imgSrcId) != -1) {
wantedImg = allImages[j]; break;}
}
bodyDiv.getElementsByTagName('img')[i].src = 'data:' + wantedImg.ContentType + ';base64,' + wantedImg.ContentBytes;
}
}
setAsync......
}
}}}})})};
Second question
The problem with addFileAttachmentAsync that this is work only with files that is on external server, and it's not add a blob , local files.
So also here the solution is with Outlook rest API . The solution will attach our file to the message but we can't see this-no preview of the attachment in message , but when we send it this will attach to message , and we could see in our message that the attachment is there.
The solution is really similar to the one of the image in body - Save the message as a draft , get access token and this time the Http Request will be 'POST' request to our message id to attach our file to the current message.
Code to the request to add attachment to message ( all the way until here is the same like question 1):
var attachment ={
"#odata.type": "#Microsoft.OutlookServices.FileAttachment",
"Name": "smile.png",
"ContentBytes": "AAACFAMxLjAeKUDndY7EKF4P7QiWE7HgHLa7UiropGUTiDp5V07M0c5jaaTteauhzs0hOU+EOmVT0Lb6eSQ2MzgkCre/zCV9+kIB9PjWnOzoufau67J9PQdXapsOQSMcpt9X2QpcIjnl7H3sLu9iu2rqcvSjwhDnK6JygtghUB405EZHZ9LQcfJ1ZTYHylke2T9zbViq2BPqU/8IHZWsb/KQ/qzV4Jwv3NHnI583JvOuAtETJngh964edC4cU2IY6FkIWprksRw7d4fEQ/+3KbEyW0trIZm59jpTSV01/PhOI0RDKj1xI1Vr+lgMRZpOrYDfChWWWbByNzSXbIsTjHMU6GmQ5Cb09H3kv/2koFa5Pj2z8i+NGywYKw8ZSu3NVblM9I0EkQVLrxkM8gqyrDEtAobxPRxEzGTEXdnjws5UIiiGFBq3khuxejFGCNvUbmPM9guVZO0ccDe1FICTFHkrPlLZW/TvJYMou0HBrvH7s4taBHyZw5x03dhps+WG19D5na44vaVX2Vni6ZrrxfqFo7JTUpCJxCcPyoG7/nEWtJ/V/J+oXdypeapN9Agl6Q81WvCbzuyZgbLTfj6NXWDoliie069Hvk/k2lP+HyO7Iu5ffeRX2WWguwdfGXiNbqInrxn18tX+N7/KqWbRJv96tmijdCmCvsF9Lpr9k7QFKB93wuHfTuE6Qi2IVNBfzNBaz1iJYjY="
}
var xhr4 = new XMLHttpRequest();
xhr4.open("POST", "https://outlook.office.com/api/v2.0/me/messages/" + itemId + "/attachments", true);
xhr4.setRequestHeader("Content-type", "application/json");
xhr4.setRequestHeader("Access-Control-Allow-Origin", "*");
xhr4.setRequestHeader("Authorization", "Bearer " + accessToken);
xhr4.send(JSON.stringify(attachment));
xhr4.onreadystatechange = function () {
if (xhr4.readyState == 4) {
if (xhr4.status == 200)
console.log("ok");
else
console.log(xhr4.response);
}};
Hope it's will help someone , good luck !

Getting file contents when using DropzoneJS

I really love the DropZoneJS component and am currently wrapping it in an EmberJS component (you can see demo here). In any event, the wrapper works just fine but I wanted to listen in on one of Dropzone's events and introspect the file contents (not the meta info like size, lastModified, etc.). The file type I'm dealing with is an XML file and I'd like to look "into" it to validate before sending it.
How can one do that? I would have thought the contents would hang off of the file object that you can pick up on many of the events but unless I'm just missing something obvious, it isn't there. :(
This worked for me:
Dropzone.options.PDFDrop = {
maxFilesize: 10, // Mb
accept: function(file, done) {
var reader = new FileReader();
reader.addEventListener("loadend", function(event) { console.log(event.target.result);});
reader.readAsText(file);
}
};
could also use reader.reaAsBinaryString() if binary data!
Ok, I've answer my own question and since others appear interested I'll post my answer here. For a working demo of this you can find it here:
https://ui-dropzone.firebaseapp.com/demo-local-data
In the demo I've wrapped the Dropzone component in the EmberJS framework but if you look at the code you'll find it's just Javascript code, nothing much to be afraid of. :)
The things we'll do are:
Get the file before the network request
The key thing we need become familiar with is the HTML5 API. Good news is it is quite simple. Take a look at this code and maybe that's all you need:
/**
* Replaces the XHR's send operation so that the stream can be
* retrieved on the client side instead being sent to the server.
* The function name is a little confusing (other than it replaces the "send"
* from Dropzonejs) because really what it's doing is reading the file and
* NOT sending to the server.
*/
_sendIntercept(file, options={}) {
return new RSVP.Promise((resolve,reject) => {
if(!options.readType) {
const mime = file.type;
const textType = a(_textTypes).any(type => {
const re = new RegExp(type);
return re.test(mime);
});
options.readType = textType ? 'readAsText' : 'readAsDataURL';
}
let reader = new window.FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = () => {
reject(reader.result);
};
// run the reader
reader[options.readType](file);
});
},
https://github.com/lifegadget/ui-dropzone/blob/0.7.2/addon/mixins/xhr-intercept.js#L10-L38
The code above returns a Promise which resolves once the file that's been dropped into the browser has been "read" into Javascript. This should be very quick as it's all local (do be aware that if you're downloading really large files you might want to "chunk" it ... that's a more advanced topic).
Hook into Dropzone
Now we need to find somewhere to hook into in Dropzone to read the file contents and stop the network request that we no longer need. Since the HTML5 File API just needs a File object you'll notice that Dropzone provides all sorts of hooks for that.
I decided on the "accept" hook because it would give me the opportunity to download the file and validate all in one go (for me it's mainly about drag and dropping XML's and so the content of the file is a part of the validation process) and crucially it happens before the network request.
Now it's important you realise that we're "replacing" the accept function not listening to the event it fires. If we just listened we would still incur a network request. So to **overload* accept we do something like this:
this.accept = this.localAcceptHandler; // replace "accept" on Dropzone
This will only work if this is the Dropzone object. You can achieve that by:
including it in your init hook function
including it as part of your instantiation (e.g., new Dropzone({accept: {...})
Now we've referred to the "localAcceptHandler", let me introduce it to you:
localAcceptHandler(file, done) {
this._sendIntercept(file).then(result => {
file.contents = result;
if(typeOf(this.localSuccess) === 'function') {
this.localSuccess(file, done);
} else {
done(); // empty done signals success
}
}).catch(result => {
if(typeOf(this.localFailure) === 'function') {
file.contents = result;
this.localFailure(file, done);
} else {
done(`Failed to download file ${file.name}`);
console.warn(file);
}
});
}
https://github.com/lifegadget/ui-dropzone/blob/0.7.2/addon/mixins/xhr-intercept.js#L40-L64
In quick summary it does the following:
read the contents of the file (aka, _sendIntercept)
based on mime type read the file either via readAsText or readAsDataURL
save the file contents to the .contents property of the file
Stop the send
To intercept the sending of the request on the network but still maintain the rest of the workflow we will replace a function called submitRequest. In the Dropzone code this function is a one liner and what I did was replace it with my own one-liner:
this._finished(files,'locally resolved, refer to "contents" property');
https://github.com/lifegadget/ui-dropzone/blob/0.7.2/addon/mixins/xhr-intercept.js#L66-L70
Provide access to retrieved document
The last step is just to ensure that our localAcceptHandler is put in place of the accept routine that dropzone supplies:
https://github.com/lifegadget/ui-dropzone/blob/0.7.2/addon/components/drop-zone.js#L88-L95
using the FileReader() solution is working amazingly good for me:
Dropzone.autoDiscover = false;
var dz = new Dropzone("#demo-upload",{
autoProcessQueue:false,
url:'upload.php'
});
dz.on("drop",function drop(e) {
var files = [];
for (var i = 0; i < e.dataTransfer.files.length; i++) {
files[i] = e.dataTransfer.files[i];
}
var reader = new FileReader();
reader.onload = function(event) {
var line = event.target.result.split('\n');
for ( var i = 0; i < line.length; i++){
console.log(line);
}
};
reader.readAsText(files[files.length-1]);

C++ builder indy intercept IdHTTPProxyServer to insert header

I want to insert header of external script over https inside browser. For those purpose i use C++ builder with IdHTTPProxyServer component. I read Document from Response event but dont know how to just insert one simple .js. -> cause Document property is read only, on the other side BeforeCommandHandler want append with script (in code under i use just text to insert for simplicity)
How to insert .js external script in C++ builder?
Here is what i done so far.
void __fastcall TForm5::IdHTTPProxyServer1HTTPResponse
(TIdHTTPProxyServerContext *AContext)
{
Memo1->Lines->Append("\nOn Response!!!\n" + AContext->Document);
}
void __fastcall TForm5::IdHTTPProxyServer1BeforeCommandHandler
(TIdCmdTCPServer *ASender, UnicodeString &AData, TIdContext *AContext) {
try {
Memo1->Lines->Add(AData);
UnicodeString sa = AData;
AData = L"<html>Something</html>" + sa;
}
catch (int e) {
MessageBeep(100);
}
}
The TIdHTTPProxyServer::OnBeforeCommandHandler event is triggered before a client's request is processed. The AData parameter is the original request. This event is meant for logging/altering commands before they are parsed.
The TIdHTTPProxyServer::OnHTTPBeforeCommand event is triggered after a client's request headers have been received but before a connection is established to the target server and the client's request body is read and sent to the server.
The TIdHTTPProxyServer::OnHTTPResponse event is triggered after a server's response headers have been received but before the response body is read and sent to the client.
So none of those events will help you.
Neither will the TIdHTTPProxyServerContext::Document property, for that matter. That property does not contain the document data, as you are assuming. It contains the server-relative URL of the document being requested instead.
To do what you are asking for, set the TIdHTTPProxyServer::DefaultTransferMode property to tmFullDocument and use the OnHTTPDocument event. The TIdHTTPProxyServerContext::TransferSource property will tell you if the data is coming from the client or the server.
You will have full access to the headers and body data and you can modify them as needed. Just make sure you update the TIdHTTPProxyServerContext.Headers, in particular the Content-Length header, if you modify the body data. The body data is provided as a TStream object (specifically, a TMemoryStream). So you can either modify the stream's content directly, or you can create a new TStream object with your desired content (the VStream parameter of the event is passed by reference so you can re-assign it).
For example:
void __fastcall TForm5::IdHTTPProxyServer1HTTPDocument(TIdHTTPProxyServerContext* AContext, TStream* &VStream)
{
if (AContext->TransferSource == tsServer)
{
String s = ReadStringAsContentType(VStream, AContext->Headers->Values[L"Content-Type"], QuoteHTTP);
// this is NOT thread-safe! TIdHTTPProxyServer is multi-threaded,
// you must synchronize with the main thread in order to safely
// update UI controls...
//
// Memo1->Text = s;
s = L"<html>Something</html>" + s;
delete VStream;
VStream = new TStringStream(s, Sysutils::TEncoding::UTF8);
AContext->Headers->Values[L"Content-Length"] = VStream->Size;
AContext->Headers->Params[L"Content-Type"][L"charset"] = L"utf-8";
}
}

How to load image list from REST API using angularJS

I have searched in this forum for quiet a bit and here's my problem -
I have a ng-repeat in my html which takes in a list of messages(Json object).
Each message JSON has a sender email address - e.g. abc#gmail.com
Now, I have to get the email address from the message and form another REST API request to fetch their images for e.g. - http://<>:8080/getImage/abc#gmail.com (email address dynamic)
So in my code, I'll have a ng-repeat and a ng-src pointing to the image REST URL
If there's no image in server, it returns a 404 and displays a broken image on the UI. How do I handle it? On the other hand, if I make a http request to determine if there's a success message and on failure return a default image, then the whole thing goes through an endless loop. I'll try to create a fiddle and include it for better explanation.
Use the error block to handle such behavior:
function($http) {
var restUrl = 'getImage/abc';
return {
fetchImage: function(imageId) {
var self = this;
return $http.get(restUrl + '/' + imageId).
success(function(data) {
return self.imageUrl = data;
}).
error(function(data) {
return self.imageUrl = "pathToDefaultImage";
});
},
...

Resources