@@ -8,6 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
88import { faCheck , faEnvelope } from '@fortawesome/free-solid-svg-icons' ;
99import { OdooTheme } from '../../../utils/Themes' ;
1010import { _t } from '../../../utils/Translator' ;
11+ import PostalMime from 'postal-mime' ;
1112
1213//total attachments size threshold in megabytes
1314const SIZE_THRESHOLD_TOTAL = 40 ;
@@ -33,132 +34,115 @@ class Logger extends React.Component<LoggerProps, LoggerState> {
3334 } ;
3435 }
3536
36- private fetchAttachmentContent ( attachment , index ) : Promise < any > {
37- return new Promise < any > ( ( resolve ) => {
38- if ( attachment . size > SIZE_THRESHOLD_SINGLE_ELEMENT * 1024 * 1024 ) {
39- resolve ( {
40- name : attachment . name ,
41- inline : attachment . isInline && attachment . contentType . indexOf ( 'image' ) >= 0 ,
42- oversize : true ,
43- index : index ,
44- } ) ;
45- }
46- Office . context . mailbox . item . getAttachmentContentAsync ( attachment . id , ( asyncResult ) => {
47- resolve ( {
48- name : attachment . name ,
49- content : asyncResult . value . content ,
50- inline : attachment . isInline && attachment . contentType . indexOf ( 'image' ) >= 0 ,
51- oversize : false ,
52- index : index ,
53- } ) ;
54- } ) ;
55- } ) ;
37+ private arrayBufferToBase64 ( buffer ) {
38+ const bytes = new Uint8Array ( buffer ) ;
39+ const chunkSize = 0x8000 ; // 32KB
40+ let binary = '' ;
41+
42+ for ( let i = 0 ; i < bytes . length ; i += chunkSize ) {
43+ binary += String . fromCharCode ( ...bytes . subarray ( i , i + chunkSize ) ) ;
44+ }
45+
46+ return btoa ( binary ) ;
5647 }
5748
5849 private logRequest = async ( event ) : Promise < any > => {
5950 event . stopPropagation ( ) ;
6051
6152 this . setState ( { logged : 1 } ) ;
62- Office . context . mailbox . item . body . getAsync ( Office . CoercionType . Html , async ( result ) => {
53+ Office . context . mailbox . item . getAsFileAsync ( async ( result ) => {
54+ if ( ! result . value && result . error ) {
55+ this . context . showHttpErrorMessage ( result . error ) ;
56+ this . setState ( { logged : 0 } ) ;
57+ return ;
58+ }
59+
60+ const parser = new PostalMime ( ) ;
61+ const email = await parser . parse ( atob ( result . value ) ) ;
62+ const doc = new DOMParser ( ) . parseFromString ( email . html , 'text/html' ) ;
63+
64+ let node : Element = doc . getElementById ( 'appendonsend' ) ;
65+ // Remove the history and only log the most recent message.
66+ while ( node ) {
67+ const next = node . nextElementSibling ;
68+ node . parentNode . removeChild ( node ) ;
69+ node = next ;
70+ }
6371 const msgHeader = `<div>${ _t ( 'From : %(email)s' , {
64- email : Office . context . mailbox . item . sender . emailAddress ,
72+ email : email . from . address ,
6573 } ) } </div>`;
74+ doc . body . insertAdjacentHTML ( 'afterbegin' , msgHeader ) ;
6675 const msgFooter = `<br/><div class="text-muted font-italic">${ _t (
6776 'Logged from' ,
6877 ) } <a href="https://www.odoo.com/documentation/master/applications/productivity/mail_plugins.html" target="_blank">${ _t (
6978 'Outlook Inbox' ,
7079 ) } </a></div>`;
71- const body = result . value . split ( '<div id="x_appendonsend"></div>' ) [ 0 ] ; // Remove the history and only log the most recent message.
72- const message = msgHeader + body + msgFooter ;
73- const doc = new DOMParser ( ) . parseFromString ( message , 'text/html' ) ;
74- const officeAttachmentDetails = Office . context . mailbox . item . attachments ;
75- let totalSize = 0 ;
76- const promises : any [ ] = [ ] ;
77- const requestJson = {
78- res_id : this . props . resId ,
79- model : this . props . model ,
80- message : message ,
81- attachments : [ ] ,
82- } ;
83-
84- //check if attachment size is bigger then the threshold
85- officeAttachmentDetails . forEach ( ( officeAttachment ) => {
86- totalSize += officeAttachment . size ;
87- } ) ;
80+ doc . body . insertAdjacentHTML ( 'beforeend' , msgFooter ) ;
8881
82+ const totalSize = email . attachments . reduce ( ( sum , attachment ) => sum + attachment . content . byteLength , 0 ) ;
8983 if ( totalSize > SIZE_THRESHOLD_TOTAL * 1024 * 1024 ) {
9084 const warningMessage = _t (
9185 'Warning: Attachments could not be logged in Odoo because their total size' +
9286 ' exceeded the allowed maximum.' ,
9387 {
94- size : SIZE_THRESHOLD_SINGLE_ELEMENT ,
88+ size : SIZE_THRESHOLD_TOTAL ,
9589 } ,
9690 ) ;
9791 doc . body . innerHTML += `<div class="text-danger">${ warningMessage } </div>` ;
98- } else {
99- officeAttachmentDetails . forEach ( ( attachment , index ) => {
100- promises . push ( this . fetchAttachmentContent ( attachment , index ) ) ;
101- } ) ;
92+ email . attachments = [ ] ;
10293 }
10394
104- const results = await Promise . all ( promises ) ;
105-
106- let attachments = [ ] ;
107- let oversizeAttachments = [ ] ;
108- let inlineAttachments = [ ] ;
109-
110- results . forEach ( ( result ) => {
111- if ( result . inline ) {
112- inlineAttachments [ result . index ] = result ;
95+ const standardAttachments = [ ] ;
96+ const oversizedAttachments = [ ] ;
97+ const inlineAttachments = { } ;
98+ email . attachments . forEach ( ( attachment ) => {
99+ if ( attachment . disposition === 'inline' ) {
100+ inlineAttachments [ attachment . contentId ] = attachment ;
101+ } else if ( attachment . content . byteLength > SIZE_THRESHOLD_SINGLE_ELEMENT * 1024 * 1024 ) {
102+ oversizedAttachments . push ( attachment . filename ) ;
113103 } else {
114- if ( result . oversize ) {
115- oversizeAttachments . push ( {
116- name : result . name ,
117- } ) ;
118- } else {
119- attachments . push ( [ result . name , result . content ] ) ;
120- }
121- }
122- } ) ;
123- // a counter is needed to map img tags with attachments, as outlook does not provide
124- // an id that enables us to match an img with an attachment.
125- let j = 0 ;
126- const imageElements = doc . getElementsByTagName ( 'img' ) ;
127-
128- inlineAttachments . forEach ( ( inlineAttachment ) => {
129- if ( inlineAttachment != null && inlineAttachment . error == undefined ) {
130- if ( inlineAttachment . oversize ) {
131- imageElements [ j ] . setAttribute (
132- 'alt' ,
133- _t ( 'Could not display image %(attachmentName)s, size is over limit' , {
134- attachmentName : inlineAttachment . name ,
135- } ) ,
136- ) ;
137- } else {
138- const fileExtension = inlineAttachment . name . split ( '.' ) [ 1 ] ;
139- imageElements [ j ] . setAttribute (
140- 'src' ,
141- `data:image/${ fileExtension } ;base64, ${ inlineAttachment . content } ` ,
142- ) ;
143- }
144- j ++ ;
104+ standardAttachments . push ( [ attachment . filename , attachment . content ] ) ;
145105 }
146106 } ) ;
147107
148- if ( oversizeAttachments . length > 0 ) {
149- const attachmentNames = oversizeAttachments . map ( ( attachment ) => `"${ attachment . name } "` ) . join ( ', ' ) ;
108+ if ( oversizedAttachments . length > 0 ) {
150109 const warningMessage = _t (
151110 'Warning: Could not fetch the attachments %(attachments)s as their sizes are bigger then the maximum size of %(size)sMB per each attachment.' ,
152111 {
153- attachments : attachmentNames ,
154- size : SIZE_THRESHOLD_TOTAL ,
112+ attachments : oversizedAttachments . join ( ', ' ) ,
113+ size : SIZE_THRESHOLD_SINGLE_ELEMENT ,
155114 } ,
156115 ) ;
157116 doc . body . innerHTML += `<div class="text-danger">${ warningMessage } </div>` ;
158117 }
159118
160- requestJson . message = doc . body . innerHTML ;
161- requestJson . attachments = attachments ;
119+ const imageElements = Array . from ( doc . getElementsByTagName ( 'img' ) ) . filter ( ( img ) =>
120+ img . getAttribute ( 'src' ) ?. startsWith ( 'cid:' ) ,
121+ ) ;
122+ imageElements . forEach ( ( element ) => {
123+ const attachment = inlineAttachments [ `<${ element . src . replace ( / ^ c i d : / , '' ) } >` ] ;
124+ if ( attachment ?. content . byteLength > SIZE_THRESHOLD_SINGLE_ELEMENT * 1024 * 1024 ) {
125+ element . setAttribute (
126+ 'alt' ,
127+ _t ( 'Could not display image %(attachmentName)s, size is over limit' , {
128+ attachmentName : attachment . filename ,
129+ } ) ,
130+ ) ;
131+ } else if ( attachment ) {
132+ const fileExtension = attachment . filename . split ( '.' ) [ 1 ] ;
133+ element . setAttribute (
134+ 'src' ,
135+ `data:image/${ fileExtension } ;base64, ${ this . arrayBufferToBase64 ( attachment . content ) } ` ,
136+ ) ;
137+ }
138+ } ) ;
139+
140+ const requestJson = {
141+ res_id : this . props . resId ,
142+ model : this . props . model ,
143+ message : doc . documentElement . outerHTML ,
144+ attachments : standardAttachments ,
145+ } ;
162146
163147 const logRequest = sendHttpRequest (
164148 HttpVerb . POST ,
0 commit comments