@@ -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,115 @@ 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 ) => sum + attachment . content . byteLength , 0 ) ;
89
83
if ( totalSize > SIZE_THRESHOLD_TOTAL * 1024 * 1024 ) {
90
84
const warningMessage = _t (
91
85
'Warning: Attachments could not be logged in Odoo because their total size' +
92
86
' exceeded the allowed maximum.' ,
93
87
{
94
- size : SIZE_THRESHOLD_SINGLE_ELEMENT ,
88
+ size : SIZE_THRESHOLD_TOTAL ,
95
89
} ,
96
90
) ;
97
91
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 = [ ] ;
102
93
}
103
94
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 ) ;
113
103
} 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 ] ) ;
145
105
}
146
106
} ) ;
147
107
148
- if ( oversizeAttachments . length > 0 ) {
149
- const attachmentNames = oversizeAttachments . map ( ( attachment ) => `"${ attachment . name } "` ) . join ( ', ' ) ;
108
+ if ( oversizedAttachments . length > 0 ) {
150
109
const warningMessage = _t (
151
110
'Warning: Could not fetch the attachments %(attachments)s as their sizes are bigger then the maximum size of %(size)sMB per each attachment.' ,
152
111
{
153
- attachments : attachmentNames ,
154
- size : SIZE_THRESHOLD_TOTAL ,
112
+ attachments : oversizedAttachments . join ( ', ' ) ,
113
+ size : SIZE_THRESHOLD_SINGLE_ELEMENT ,
155
114
} ,
156
115
) ;
157
116
doc . body . innerHTML += `<div class="text-danger">${ warningMessage } </div>` ;
158
117
}
159
118
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
+ } ;
162
146
163
147
const logRequest = sendHttpRequest (
164
148
HttpVerb . POST ,
0 commit comments