@@ -8,6 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8
8
import { faCheck , faEnvelope } from '@fortawesome/free-solid-svg-icons' ;
9
9
import { OdooTheme } from '../../../utils/Themes' ;
10
10
import { _t } from '../../../utils/Translator' ;
11
+ import PostalMime from 'postal-mime' ;
11
12
12
13
//total attachments size threshold in megabytes
13
14
const SIZE_THRESHOLD_TOTAL = 40 ;
@@ -33,132 +34,117 @@ class Logger extends React.Component<LoggerProps, LoggerState> {
33
34
} ;
34
35
}
35
36
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 ) ;
56
47
}
57
48
58
49
private logRequest = async ( event ) : Promise < any > => {
59
50
event . stopPropagation ( ) ;
60
51
61
52
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
+ }
63
71
const msgHeader = `<div>${ _t ( 'From : %(email)s' , {
64
- email : Office . context . mailbox . item . sender . emailAddress ,
72
+ email : email . from . address ,
65
73
} ) } </div>`;
74
+ doc . body . insertAdjacentHTML ( 'afterbegin' , msgHeader ) ;
66
75
const msgFooter = `<br/><div class="text-muted font-italic">${ _t (
67
76
'Logged from' ,
68
77
) } <a href="https://www.odoo.com/documentation/master/applications/productivity/mail_plugins.html" target="_blank">${ _t (
69
78
'Outlook Inbox' ,
70
79
) } </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 ) ;
88
81
82
+ const totalSize = email . attachments . reduce ( ( sum , attachment ) => {
83
+ return sum + attachment . content . byteLength ;
84
+ } , 0 ) ;
89
85
if ( totalSize > SIZE_THRESHOLD_TOTAL * 1024 * 1024 ) {
90
86
const warningMessage = _t (
91
87
'Warning: Attachments could not be logged in Odoo because their total size' +
92
88
' exceeded the allowed maximum.' ,
93
89
{
94
- size : SIZE_THRESHOLD_SINGLE_ELEMENT ,
90
+ size : SIZE_THRESHOLD_TOTAL ,
95
91
} ,
96
92
) ;
97
93
doc . body . innerHTML += `<div class="text-danger">${ warningMessage } </div>` ;
98
- } else {
99
- officeAttachmentDetails . forEach ( ( attachment , index ) => {
100
- promises . push ( this . fetchAttachmentContent ( attachment , index ) ) ;
101
- } ) ;
94
+ email . attachments = [ ] ;
102
95
}
103
96
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 ;
97
+ const standardAttachments = [ ] ;
98
+ const oversizedAttachments = [ ] ;
99
+ const inlineAttachments = { } ;
100
+ for ( const attachment of email . attachments ) {
101
+ if ( attachment . disposition === 'inline' ) {
102
+ inlineAttachments [ attachment . contentId ] = attachment ;
103
+ } else if ( attachment . content . byteLength > SIZE_THRESHOLD_SINGLE_ELEMENT * 1024 * 1024 ) {
104
+ oversizedAttachments . push ( attachment . filename ) ;
113
105
} 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 ++ ;
106
+ standardAttachments . push ( [ attachment . filename , attachment . content ] ) ;
145
107
}
146
- } ) ;
108
+ }
147
109
148
- if ( oversizeAttachments . length > 0 ) {
149
- const attachmentNames = oversizeAttachments . map ( ( attachment ) => `"${ attachment . name } "` ) . join ( ', ' ) ;
110
+ if ( oversizedAttachments . length > 0 ) {
150
111
const warningMessage = _t (
151
112
'Warning: Could not fetch the attachments %(attachments)s as their sizes are bigger then the maximum size of %(size)sMB per each attachment.' ,
152
113
{
153
- attachments : attachmentNames ,
154
- size : SIZE_THRESHOLD_TOTAL ,
114
+ attachments : oversizedAttachments . join ( ', ' ) ,
115
+ size : SIZE_THRESHOLD_SINGLE_ELEMENT ,
155
116
} ,
156
117
) ;
157
118
doc . body . innerHTML += `<div class="text-danger">${ warningMessage } </div>` ;
158
119
}
159
120
160
- requestJson . message = doc . body . innerHTML ;
161
- requestJson . attachments = attachments ;
121
+ const imageElements = Array . from ( doc . getElementsByTagName ( 'img' ) ) . filter ( ( img ) =>
122
+ img . getAttribute ( 'src' ) ?. startsWith ( 'cid:' ) ,
123
+ ) ;
124
+ imageElements . forEach ( ( element ) => {
125
+ const attachment = inlineAttachments [ `<${ element . src . replace ( / ^ c i d : / , '' ) } >` ] ;
126
+ if ( attachment ?. content . byteLength > SIZE_THRESHOLD_SINGLE_ELEMENT * 1024 * 1024 ) {
127
+ element . setAttribute (
128
+ 'alt' ,
129
+ _t ( 'Could not display image %(attachmentName)s, size is over limit' , {
130
+ attachmentName : attachment . filename ,
131
+ } ) ,
132
+ ) ;
133
+ } else if ( attachment ) {
134
+ const fileExtension = attachment . filename . split ( '.' ) [ 1 ] ;
135
+ element . setAttribute (
136
+ 'src' ,
137
+ `data:image/${ fileExtension } ;base64, ${ this . arrayBufferToBase64 ( attachment . content ) } ` ,
138
+ ) ;
139
+ }
140
+ } ) ;
141
+
142
+ const requestJson = {
143
+ res_id : this . props . resId ,
144
+ model : this . props . model ,
145
+ message : doc . documentElement . outerHTML ,
146
+ attachments : standardAttachments ,
147
+ } ;
162
148
163
149
const logRequest = sendHttpRequest (
164
150
HttpVerb . POST ,
0 commit comments