2014-11-25 22:09:55 +01:00
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
( function ( ) {
'use strict' ;
/ * *
* @ name Message
* @ constructor
* @ param { string } accountId - the account ID
* @ param { string } mailboxPath - an array of the mailbox path components
* @ param { object } futureAddressBookData - either an object literal or a promise
2015-08-26 17:56:47 +02:00
* @ param { bool } lazy - do "lazy loading" so we are very quick at initializing message instances
2014-11-25 22:09:55 +01:00
* /
2015-08-26 17:56:47 +02:00
function Message ( accountId , mailbox , futureMessageData , lazy ) {
2014-11-25 22:09:55 +01:00
this . accountId = accountId ;
2014-12-16 21:47:34 +01:00
this . $mailbox = mailbox ;
2015-03-18 18:41:14 +01:00
this . $hasUnsafeContent = false ;
this . $loadUnsafeContent = false ;
2015-04-18 14:50:06 +02:00
this . editable = { to : [ ] , cc : [ ] , bcc : [ ] } ;
2015-08-26 17:56:47 +02:00
this . selected = false ;
2014-11-25 22:09:55 +01:00
// Data is immediately available
if ( typeof futureMessageData . then !== 'function' ) {
//console.debug(JSON.stringify(futureMessageData, undefined, 2));
2016-01-07 21:43:26 +01:00
if ( angular . isUndefined ( lazy ) || ! lazy ) {
2018-05-15 05:18:30 +02:00
this . init ( futureMessageData ) ;
2015-08-26 17:56:47 +02:00
}
2016-01-07 21:43:26 +01:00
this . uid = parseInt ( futureMessageData . uid ) ;
2019-06-27 23:43:11 +02:00
this . level = parseInt ( futureMessageData . level ) ;
this . first = parseInt ( futureMessageData . first ) === 1 ;
if ( this . first ) {
this . threadCount = parseInt ( futureMessageData . count ) ;
this . collapsed = ( futureMessageData . collapsed === true ) ;
}
else if ( ! isNaN ( this . level ) && this . level >= 0 ) {
this . threadMember = true ;
}
2014-11-25 22:09:55 +01:00
}
else {
// The promise will be unwrapped first
this . $unwrap ( futureMessageData ) ;
}
}
/ * *
* @ memberof Message
* @ desc The factory we ' ll use to register with Angular
* @ returns the Message constructor
* /
2016-06-22 03:29:44 +02:00
Message . $factory = [ '$q' , '$timeout' , '$log' , 'sgSettings' , 'sgMessage_STATUS' , 'Resource' , 'Preferences' , function ( $q , $timeout , $log , Settings , Message _STATUS , Resource , Preferences ) {
2014-11-25 22:09:55 +01:00
angular . extend ( Message , {
2016-05-30 18:29:58 +02:00
STATUS : Message _STATUS ,
2014-11-25 22:09:55 +01:00
$q : $q ,
$timeout : $timeout ,
$log : $log ,
2016-06-22 03:29:44 +02:00
$$resource : new Resource ( Settings . activeUser ( 'folderURL' ) + 'Mail' , Settings . activeUser ( ) ) ,
2018-01-24 04:23:14 +01:00
$Preferences : Preferences ,
2016-06-22 03:29:44 +02:00
$avatar : angular . bind ( Preferences , Preferences . avatar )
2015-08-03 17:53:54 +02:00
} ) ;
2017-06-01 19:46:57 +02:00
2015-08-03 17:53:54 +02:00
// Initialize tags form user's defaults
2017-06-01 19:46:57 +02:00
if ( Preferences . defaults . SOGoMailLabelsColors ) {
Message . $tags = Preferences . defaults . SOGoMailLabelsColors ;
}
if ( Preferences . defaults . SOGoMailDisplayRemoteInlineImages &&
Preferences . defaults . SOGoMailDisplayRemoteInlineImages == 'always' ) {
Message . $displayRemoteInlineImages = true ;
}
2015-05-05 21:22:52 +02:00
2014-11-25 22:09:55 +01:00
return Message ; // return constructor
} ] ;
2015-07-29 21:16:43 +02:00
/ * *
* @ module SOGo . MailerUI
* @ desc Factory registration of Message in Angular module .
* /
try {
angular . module ( 'SOGo.MailerUI' ) ;
}
catch ( e ) {
angular . module ( 'SOGo.MailerUI' , [ 'SOGo.Common' ] ) ;
}
2014-11-25 22:09:55 +01:00
angular . module ( 'SOGo.MailerUI' )
2016-05-30 18:29:58 +02:00
. constant ( 'sgMessage_STATUS' , {
NOT _LOADED : 0 ,
DELAYED _LOADING : 1 ,
LOADING : 2 ,
LOADED : 3 ,
DELAYED _MS : 300
} )
2015-05-06 04:06:13 +02:00
. factory ( 'Message' , Message . $factory ) ;
2014-11-25 22:09:55 +01:00
2015-05-04 19:56:29 +02:00
/ * *
* @ function filterTags
* @ memberof Message . prototype
* @ desc Search for tags ( ie . , mail labels ) matching some criterias
* @ param { string } search - the search string to match
* @ returns a collection of strings
* /
2016-03-28 16:42:40 +02:00
Message . filterTags = function ( query , excludedTags ) {
2015-09-10 17:44:09 +02:00
var re = new RegExp ( query , 'i' ) ,
results = [ ] ;
_ . forEach ( _ . keys ( Message . $tags ) , function ( tag ) {
var pair = Message . $tags [ tag ] ;
if ( pair [ 0 ] . search ( re ) != - 1 ) {
2016-03-28 16:42:40 +02:00
if ( ! _ . includes ( excludedTags , tag ) )
results . push ( { name : tag , description : pair [ 0 ] , color : pair [ 1 ] } ) ;
2015-09-10 17:44:09 +02:00
}
2015-05-04 19:56:29 +02:00
} ) ;
2016-03-28 16:42:40 +02:00
2015-09-10 17:44:09 +02:00
return results ;
2015-05-04 19:56:29 +02:00
} ;
2018-05-15 05:18:30 +02:00
/ * *
* @ function init
* @ memberof Message . prototype
* @ desc Extend instance with new data and massage some attributes .
* @ param { object } data - attributes of message
* /
Message . prototype . init = function ( data ) {
var _this = this ;
angular . extend ( this , data ) ;
this . $formatFullAddresses ( ) ;
this . $loadUnsafeContent = false ;
_ . forEach ( this . flags , function ( flag , i ) {
if ( flag . charAt ( 0 ) == '$' ) {
_this . flags . splice ( i , 1 , '_' + flag ) ;
}
} ) ;
} ;
2014-11-25 22:09:55 +01:00
/ * *
* @ function $absolutePath
* @ memberof Message . prototype
* @ desc Build the path of the message
* @ returns a string representing the path relative to the mail module
* /
2014-12-11 15:15:33 +01:00
Message . prototype . $absolutePath = function ( options ) {
2016-05-10 20:41:24 +02:00
var _this = this , id = this . id ;
function buildPath ( ) {
2015-08-26 17:56:47 +02:00
var path ;
2016-05-10 20:41:24 +02:00
path = _ . map ( _this . $mailbox . path . split ( '/' ) , function ( component ) {
2015-08-26 17:56:47 +02:00
return 'folder' + component . asCSSIdentifier ( ) ;
} ) ;
2016-05-10 20:41:24 +02:00
path . splice ( 0 , 0 , _this . accountId ) ; // insert account ID
return path . join ( '/' ) ;
}
2014-11-25 22:09:55 +01:00
2016-05-10 20:41:24 +02:00
if ( angular . isUndefined ( this . id ) || options && options . nocache ) {
this . id = buildPath ( ) + '/' + this . uid ; // add message UID
id = this . id ;
}
if ( options && options . asDraft && this . draftId ) {
id = buildPath ( ) + '/' + this . draftId ; // add draft ID
2014-12-11 15:15:33 +01:00
}
2017-03-07 16:26:10 +01:00
if ( options && options . withResourcePath ) {
id = Message . $$resource . path ( id ) ; // return absolute URL
}
2014-11-25 22:09:55 +01:00
2016-05-10 20:41:24 +02:00
return id ;
2014-11-25 22:09:55 +01:00
} ;
2014-12-16 21:47:34 +01:00
/ * *
* @ function $setUID
* @ memberof Message . prototype
* @ desc Change the UID of the message . This happens when saving a draft .
* @ param { number } uid - the new message UID
* /
Message . prototype . $setUID = function ( uid ) {
2016-05-10 20:41:24 +02:00
var oldUID = ( this . uid || - 1 ) , _this = this , index ;
2014-12-16 21:47:34 +01:00
2015-12-01 17:19:18 +01:00
if ( oldUID != parseInt ( uid ) ) {
2016-01-07 21:43:26 +01:00
this . uid = parseInt ( uid ) ;
2016-05-10 20:41:24 +02:00
this . $absolutePath ( { nocache : true } ) ;
2015-12-01 17:19:18 +01:00
if ( oldUID > - 1 ) {
oldUID = oldUID . toString ( ) ;
if ( angular . isDefined ( this . $mailbox . uidsMap [ oldUID ] ) ) {
2016-05-10 20:41:24 +02:00
index = this . $mailbox . uidsMap [ oldUID ] ;
this . $mailbox . uidsMap [ uid ] = index ;
2015-12-01 17:19:18 +01:00
delete this . $mailbox . uidsMap [ oldUID ] ;
2019-01-21 22:24:07 +01:00
this . $mailbox . $messages [ index ] . uid = this . uid ;
2016-05-10 20:41:24 +02:00
// Update messages list of mailbox
_ . forEach ( [ 'from' , 'to' , 'subject' ] , function ( attr ) {
2019-01-21 22:24:07 +01:00
_this . $mailbox . $messages [ index ] [ attr ] = _this . editable [ attr ] ;
2016-05-10 20:41:24 +02:00
} ) ;
2015-12-01 17:19:18 +01:00
}
2014-12-16 21:47:34 +01:00
}
2015-12-01 22:26:30 +01:00
else {
// Refresh selected folder if it's the drafts mailbox
2016-01-07 21:43:26 +01:00
if ( this . $mailbox . constructor . selectedFolder &&
this . $mailbox . constructor . selectedFolder . type == 'draft' ) {
2015-12-01 22:26:30 +01:00
this . $mailbox . constructor . selectedFolder . $filter ( ) ;
}
}
2014-12-16 21:47:34 +01:00
}
} ;
2014-12-11 15:15:33 +01:00
/ * *
* @ function $formatFullAddresses
* @ memberof Message . prototype
2014-12-12 03:52:46 +01:00
* @ desc Format all sender and recipients addresses with a complete description ( name < email > ) .
2016-06-22 03:29:44 +02:00
* This function also generates the avatar URL for each email address and a short name
2014-12-11 15:15:33 +01:00
* /
Message . prototype . $formatFullAddresses = function ( ) {
var _this = this ;
2016-03-03 19:38:54 +01:00
var identities = _ . map ( _this . $mailbox . $account . identities , 'email' ) ;
2014-12-11 15:15:33 +01:00
// Build long representation of email addresses
2016-03-03 19:38:54 +01:00
_ . forEach ( [ 'from' , 'to' , 'cc' , 'bcc' , 'reply-to' ] , function ( type ) {
_ . forEach ( _this [ type ] , function ( data ) {
2015-08-21 19:37:18 +02:00
if ( data . name && data . name != data . email ) {
2014-12-11 15:15:33 +01:00
data . full = data . name + ' <' + data . email + '>' ;
2015-08-21 19:37:18 +02:00
2016-02-26 21:19:33 +01:00
if ( data . name . length < 10 )
// Name is already short
data . shortname = data . name ;
else if ( data . name . split ( ' ' ) . length )
2016-06-22 03:29:44 +02:00
// If we have "Alice Foo" or "Foo, Alice" as name, we grab "Alice"
data . shortname = _ . first ( _ . last ( data . name . split ( /, */ ) ) . split ( / +/ ) ) . replace ( '\'' , '' ) ;
2015-08-21 19:37:18 +02:00
}
2016-02-03 15:00:08 +01:00
else if ( data . email ) {
2014-12-11 15:15:33 +01:00
data . full = '<' + data . email + '>' ;
2015-08-21 19:37:18 +02:00
data . shortname = data . email . split ( '@' ) [ 0 ] ;
}
2016-06-22 03:29:44 +02:00
data . image = Message . $avatar ( data . email , 32 ) ;
2015-08-21 19:37:18 +02:00
// If the current user is the recepient, overwrite
// the short name with 'me'
if ( _ . indexOf ( identities , data . email ) >= 0 )
2015-08-22 13:51:13 +02:00
data . shortname = l ( 'me' ) ;
2015-08-21 19:37:18 +02:00
} ) ;
} ) ;
} ;
/ * *
* @ function $shortRecipients
* @ memberof Message . prototype
* @ desc Format all recipients into a very compact string
* @ returns a compacted string of all recipients
* /
2016-02-26 21:19:33 +01:00
Message . prototype . $shortRecipients = function ( max ) {
var _this = this , result = [ ] , count = 0 , total = 0 ;
2015-08-21 19:37:18 +02:00
2016-02-26 21:19:33 +01:00
// Build short representation of email addresses
2016-03-03 19:38:54 +01:00
_ . forEach ( [ 'to' , 'cc' , 'bcc' ] , function ( type ) {
2016-02-26 21:19:33 +01:00
total += _this [ type ] ? _this [ type ] . length : 0 ;
2016-03-03 19:38:54 +01:00
_ . forEach ( _this [ type ] , function ( data , i ) {
2016-02-26 21:19:33 +01:00
if ( count < max )
result . push ( data . shortname ) ;
count ++ ;
2014-12-11 15:15:33 +01:00
} ) ;
} ) ;
2015-08-21 19:37:18 +02:00
2016-02-26 21:19:33 +01:00
if ( total > max )
result . push ( l ( 'and %{0} more...' , ( total - max ) ) ) ;
2015-08-21 19:37:18 +02:00
return result . join ( ', ' ) ;
2014-12-11 15:15:33 +01:00
} ;
2014-12-06 04:49:08 +01:00
/ * *
* @ function $shortAddress
* @ memberof Message . prototype
* @ desc Format the first address of a specific type with a short description .
* @ returns a string of the name or the email of the envelope address type
* /
Message . prototype . $shortAddress = function ( type ) {
var address = '' ;
if ( this [ type ] && this [ type ] . length > 0 ) {
address = this [ type ] [ 0 ] . name || this [ type ] [ 0 ] . email || '' ;
}
return address ;
} ;
2015-11-24 22:30:36 +01:00
/ * *
* @ function allowReplyAll
* @ memberof Message . prototype
* @ desc Check if 'Reply to All' is an appropriate action on the message .
* @ returns true if the message is not a draft and has more than one recipient
* /
Message . prototype . allowReplyAll = function ( ) {
2019-02-26 20:39:25 +01:00
var identities = _ . map ( this . $mailbox . $account . identities , 'email' ) ;
2015-11-24 22:30:36 +01:00
var recipientsCount = 0 ;
2019-02-26 20:39:25 +01:00
recipientsCount = _ . reduce ( [ 'to' , 'cc' , 'bcc' , 'reply-to' ] , _ . bind ( function ( count , type ) {
var typeCount = 0 ;
if ( this [ type ] ) {
typeCount = this [ type ] . length ;
_ . forEach ( this [ type ] , function ( recipient ) {
if ( _ . indexOf ( identities , recipient . email ) >= 0 ) {
typeCount -- ;
}
} ) ;
return count + typeCount ;
}
else {
2015-11-24 22:30:36 +01:00
return count ;
2019-02-26 20:39:25 +01:00
}
2016-03-03 19:38:54 +01:00
} , this ) , recipientsCount ) ;
2015-11-24 22:30:36 +01:00
return ! this . isDraft && recipientsCount > 1 ;
} ;
2015-03-18 18:41:14 +01:00
/ * *
* @ function loadUnsafeContent
* @ memberof Message . prototype
* @ desc Mark the message to load unsafe resources when calling $content ( ) .
* /
Message . prototype . loadUnsafeContent = function ( ) {
this . $loadUnsafeContent = true ;
2018-01-24 16:32:36 +01:00
delete this . $parts ;
2015-03-18 18:41:14 +01:00
} ;
2014-11-25 22:09:55 +01:00
/ * *
* @ function $content
* @ memberof Message . prototype
2014-12-11 17:24:22 +01:00
* @ desc Get the message body as accepted by SCE ( Angular Strict Contextual Escaping ) .
* @ returns the HTML representation of the body
2014-11-25 22:09:55 +01:00
* /
Message . prototype . $content = function ( ) {
2015-05-13 04:37:58 +02:00
var _this = this ,
parts = [ ] ,
_visit = function ( part ) {
2015-08-08 02:38:26 +02:00
part . msgclass = 'msg-attachment-other' ;
2015-05-13 16:36:34 +02:00
if ( part . type == 'UIxMailPartAlternativeViewer' ) {
2015-05-13 04:37:58 +02:00
_visit ( _ . find ( part . content , function ( alternatePart ) {
return part . preferredPart == alternatePart . contentType ;
} ) ) ;
2015-03-18 18:41:14 +01:00
}
2015-12-08 17:56:01 +01:00
// Can be used for UIxMailPartMixedViewer, UIxMailPartMessageViewer, and UIxMailPartSignedViewer
2015-05-13 04:37:58 +02:00
else if ( angular . isArray ( part . content ) ) {
2015-12-08 17:56:01 +01:00
if ( part . type == 'UIxMailPartSignedViewer' && part [ 'supports-smime' ] === 1 ) {
2018-01-24 16:32:36 +01:00
_this . signed = {
2018-01-23 19:30:56 +01:00
valid : part . valid ,
2018-01-09 21:55:32 +01:00
certificate : part . certificates [ part . certificates . length - 1 ] ,
2018-01-24 16:32:36 +01:00
message : part . message
2015-12-08 17:56:01 +01:00
} ;
}
2017-12-23 15:36:22 +01:00
else if ( part . type == 'UIxMailPartEncryptedViewer' ) {
2019-08-19 16:37:15 +02:00
if ( part . encrypted ) {
_this . encrypted = {
valid : part . decrypted
} ;
if ( part . decrypted )
_this . encrypted . message = l ( "This message is encrypted" ) ;
else
_this . encrypted . message = l ( "This message can't be decrypted. Please make sure you have uploaded your S/MIME certificate from the mail preferences module." ) ;
}
if ( part . opaqueSigned ) {
_this . signed = {
valid : part . valid ,
certificate : part . certificates [ part . certificates . length - 1 ] ,
message : part . message
} ;
}
2017-12-23 15:36:22 +01:00
}
2016-03-03 19:38:54 +01:00
_ . forEach ( part . content , function ( mixedPart ) {
2015-05-13 04:37:58 +02:00
_visit ( mixedPart ) ;
} ) ;
}
else {
if ( angular . isUndefined ( part . safeContent ) ) {
// Keep a copy of the original content
part . safeContent = part . content ;
2015-09-04 22:43:01 +02:00
_this . $hasUnsafeContent |= ( part . safeContent . indexOf ( ' unsafe-' ) > - 1 ) ;
2015-05-13 04:37:58 +02:00
}
2015-05-13 16:36:34 +02:00
if ( part . type == 'UIxMailPartHTMLViewer' ) {
part . html = true ;
2015-09-04 22:43:01 +02:00
if ( _this . $loadUnsafeContent || Message . $displayRemoteInlineImages ) {
2015-05-13 04:37:58 +02:00
if ( angular . isUndefined ( part . unsafeContent ) ) {
part . unsafeContent = document . createElement ( 'div' ) ;
part . unsafeContent . innerHTML = part . safeContent ;
angular . forEach ( [ 'src' , 'data' , 'classid' , 'background' , 'style' ] , function ( suffix ) {
var elements = part . unsafeContent . querySelectorAll ( '[unsafe-' + suffix + ']' ) ,
element ,
value ,
i ;
for ( i = 0 ; i < elements . length ; i ++ ) {
element = angular . element ( elements [ i ] ) ;
value = element . attr ( 'unsafe-' + suffix ) ;
element . attr ( suffix , value ) ;
element . removeAttr ( 'unsafe-' + suffix ) ;
}
} ) ;
2015-09-04 22:43:01 +02:00
_this . $hasUnsafeContent = false ;
2015-05-13 04:37:58 +02:00
}
2015-09-30 17:28:22 +02:00
part . content = part . unsafeContent . innerHTML ;
2015-05-13 04:37:58 +02:00
}
else {
2015-09-30 17:28:22 +02:00
part . content = part . safeContent ;
2015-05-13 04:37:58 +02:00
}
parts . push ( part ) ;
}
2015-05-13 16:36:34 +02:00
else if ( part . type == 'UIxMailPartICalViewer' ||
2015-05-29 16:49:40 +02:00
part . type == 'UIxMailPartImageViewer' ||
2015-05-13 16:36:34 +02:00
part . type == 'UIxMailPartLinkViewer' ) {
2015-08-04 19:52:31 +02:00
2015-08-08 02:38:26 +02:00
if ( part . type == 'UIxMailPartImageViewer' )
part . msgclass = 'msg-attachment-image' ;
2015-12-02 20:03:10 +01:00
else if ( part . type == 'UIxMailPartLinkViewer' )
part . msgclass = 'msg-attachment-link' ;
2015-08-08 02:38:26 +02:00
2015-05-13 16:36:34 +02:00
// Trusted content that can be compiled (Angularly-speaking)
part . compile = true ;
parts . push ( part ) ;
}
2015-05-13 04:37:58 +02:00
else {
2015-05-13 16:36:34 +02:00
part . html = true ;
2015-09-30 17:28:22 +02:00
part . content = part . safeContent ;
2015-05-13 04:37:58 +02:00
parts . push ( part ) ;
}
}
} ;
2016-09-23 23:17:25 +02:00
2018-01-24 16:32:36 +01:00
if ( this . $parts )
// Use the cache
return this . $parts ;
else if ( this . parts )
2016-09-23 23:17:25 +02:00
_visit ( this . parts ) ;
2015-05-13 04:37:58 +02:00
2018-01-24 16:32:36 +01:00
// Cache result
this . $parts = parts ;
2015-05-13 04:37:58 +02:00
return parts ;
2014-12-11 17:24:22 +01:00
} ;
2014-11-25 22:09:55 +01:00
2014-12-12 03:52:46 +01:00
/ * *
* @ function $editableContent
* @ memberof Message . prototype
2014-12-16 21:47:34 +01:00
* @ desc First , fetch the draft ID that corresponds to the temporary draft object on the SOGo server .
* Secondly , fetch the editable message body along with other metadata such as the recipients .
2014-12-12 03:52:46 +01:00
* @ returns the HTML representation of the body
* /
Message . prototype . $editableContent = function ( ) {
2015-08-04 16:56:55 +02:00
var _this = this ;
2014-12-12 03:52:46 +01:00
2015-08-26 17:56:47 +02:00
return Message . $$resource . fetch ( this . $absolutePath ( ) , 'edit' ) . then ( function ( data ) {
2014-12-16 21:47:34 +01:00
angular . extend ( _this , data ) ;
2015-08-04 16:56:55 +02:00
return Message . $$resource . fetch ( _this . $absolutePath ( { asDraft : true } ) , 'edit' ) . then ( function ( data ) {
2016-05-10 20:41:24 +02:00
// Try to match a known account identity from the specified "from" address
var identity = _ . find ( _this . $mailbox . $account . identities , function ( identity ) {
return data . from . toLowerCase ( ) . indexOf ( identity . email ) !== - 1 ;
} ) ;
if ( identity )
data . from = identity . full ;
2018-01-24 04:23:14 +01:00
var accountDefaults = Message . $Preferences . defaults . AuxiliaryMailAccounts [ _this . $mailbox . $account . id ] ;
if ( accountDefaults . security ) {
if ( accountDefaults . security . alwaysSign )
data . sign = true ;
if ( accountDefaults . security . alwaysEncrypt )
data . encrypt = true ;
}
2014-12-16 21:47:34 +01:00
Message . $log . debug ( 'editable = ' + JSON . stringify ( data , undefined , 2 ) ) ;
2015-04-18 14:50:06 +02:00
angular . extend ( _this . editable , data ) ;
2015-08-04 16:56:55 +02:00
return data . text ;
} ) ;
} ) ;
2014-12-12 03:52:46 +01:00
} ;
2016-02-10 22:15:00 +01:00
/ * *
* @ function $plainContent
* @ memberof Message . prototype
* @ returns the a plain text representation of the subject and body
* /
Message . prototype . $plainContent = function ( ) {
return Message . $$resource . fetch ( this . $absolutePath ( ) , 'viewplain' ) ;
} ;
2015-05-13 21:20:22 +02:00
/ * *
* @ function addTag
* @ memberof Message . prototype
* @ desc Add a mail tag on the current message .
* @ param { string } tag - the tag name
* @ returns a promise of the HTTP operation
* /
Message . prototype . addTag = function ( tag ) {
return this . $addOrRemoveTag ( 'add' , tag ) ;
} ;
/ * *
* @ function removeTag
* @ memberof Message . prototype
* @ desc Remove a mail tag from the current message .
* @ param { string } tag - the tag name
* @ returns a promise of the HTTP operation
* /
Message . prototype . removeTag = function ( tag ) {
return this . $addOrRemoveTag ( 'remove' , tag ) ;
} ;
2015-05-05 19:03:59 +02:00
/ * *
* @ function $addOrRemoveTag
* @ memberof Message . prototype
* @ desc Add or remove a mail tag on the current message .
2015-05-13 21:20:22 +02:00
* @ param { string } operation - the operation name to perform
* @ param { string } tag - the tag name
2015-05-05 19:03:59 +02:00
* @ returns a promise of the HTTP operation
* /
2015-05-04 19:56:29 +02:00
Message . prototype . $addOrRemoveTag = function ( operation , tag ) {
2015-05-13 21:20:22 +02:00
var data = {
operation : operation ,
msgUIDs : [ this . uid ] ,
2018-05-15 05:18:30 +02:00
flags : tag . replace ( /^_\$/ , '$' )
2015-05-13 21:20:22 +02:00
} ;
if ( tag )
return Message . $$resource . post ( this . $mailbox . $id ( ) , 'addOrRemoveLabel' , data ) ;
} ;
2015-05-04 19:56:29 +02:00
2015-06-04 01:42:30 +02:00
/ * *
* @ function $imipAction
* @ memberof Message . prototype
* @ desc Perform IMIP actions on the current message .
2016-12-07 20:37:59 +01:00
* @ param { string } path - the path of the IMIP calendar part
2015-06-04 01:42:30 +02:00
* @ param { string } action - the the IMIP action to perform
2016-12-07 20:37:59 +01:00
* @ param { object } data - the delegation info
2015-06-04 01:42:30 +02:00
* /
Message . prototype . $imipAction = function ( path , action , data ) {
var _this = this ;
2015-08-26 17:56:47 +02:00
Message . $$resource . post ( [ this . $absolutePath ( ) , path ] . join ( '/' ) , action , data ) . then ( function ( data ) {
2015-06-04 01:42:30 +02:00
Message . $timeout ( function ( ) {
_this . $reload ( ) ;
} ) ;
} ) ;
} ;
2015-07-08 19:19:16 +02:00
/ * *
* @ function $sendMDN
* @ memberof Message . prototype
* @ desc Send MDN response for current email message
* /
Message . prototype . $sendMDN = function ( ) {
this . shouldAskReceipt = 0 ;
2015-08-26 17:56:47 +02:00
return Message . $$resource . post ( this . $absolutePath ( ) , 'sendMDN' ) ;
2015-07-24 22:14:53 +02:00
} ;
2015-07-08 19:19:16 +02:00
2015-06-08 21:49:27 +02:00
/ * *
* @ function $deleteAttachment
* @ memberof Message . prototype
* @ desc Delete an attachment from a message being composed
* @ param { string } filename - the filename of the attachment to delete
* /
Message . prototype . $deleteAttachment = function ( filename ) {
2017-03-03 21:36:52 +01:00
var data = { 'filename' : filename } ;
2015-06-08 21:49:27 +02:00
var _this = this ;
2017-03-03 21:36:52 +01:00
Message . $$resource . fetch ( this . $absolutePath ( { asDraft : true } ) , 'deleteAttachment' , data ) . then ( function ( data ) {
2015-06-08 21:49:27 +02:00
Message . $timeout ( function ( ) {
_this . editable . attachmentAttrs = _ . filter ( _this . editable . attachmentAttrs , function ( attachment ) {
2015-07-24 22:14:53 +02:00
return attachment . filename != filename ;
} ) ;
2015-06-08 21:49:27 +02:00
} ) ;
} ) ;
2015-07-24 22:14:53 +02:00
} ;
2015-06-08 21:49:27 +02:00
2015-05-05 19:03:59 +02:00
/ * *
2019-06-27 23:43:11 +02:00
* @ function toggleFlag
2015-05-05 19:03:59 +02:00
* @ memberof Message . prototype
* @ desc Add or remove a the \ \ Flagged flag on the current message .
* @ returns a promise of the HTTP operation
* /
2015-05-13 21:20:22 +02:00
Message . prototype . toggleFlag = function ( ) {
var _this = this ,
action = 'markMessageFlagged' ;
2015-05-05 19:03:59 +02:00
2015-05-13 21:20:22 +02:00
if ( this . isflagged )
action = 'markMessageUnflagged' ;
2015-05-05 19:03:59 +02:00
2015-08-26 17:56:47 +02:00
return Message . $$resource . post ( this . $absolutePath ( ) , action ) . then ( function ( data ) {
2015-05-13 21:20:22 +02:00
Message . $timeout ( function ( ) {
_this . isflagged = ! _this . isflagged ;
} ) ;
} ) ;
2015-07-24 22:14:53 +02:00
} ;
2015-05-05 19:03:59 +02:00
2019-06-27 23:43:11 +02:00
/ * *
* @ function toggleThread
* @ memberof Message . prototype
* @ desc Collapse or expand mail thread
* @ returns a promise of the HTTP operation
* /
Message . prototype . toggleThread = function ( ) {
var _this = this ,
action = 'markMessageCollapse' ;
if ( this . collapsed )
action = 'markMessageUncollapse' ;
this . collapsed = ! this . collapsed ;
return Message . $$resource . post ( this . $absolutePath ( ) , action ) ;
} ;
2016-05-30 18:29:58 +02:00
/ * *
* @ function $isLoading
* @ memberof Message . prototype
* @ returns true if the Message content is still being retrieved from server after a specific delay
* @ see sgMessage _STATUS
* /
Message . prototype . $isLoading = function ( ) {
return this . $loaded == Message . STATUS . LOADING ;
} ;
2014-12-11 17:24:22 +01:00
/ * *
2014-12-17 20:08:43 +01:00
* @ function $reload
2014-12-11 17:24:22 +01:00
* @ memberof Message . prototype
2014-12-12 03:52:46 +01:00
* @ desc Fetch the viewable message body along with other metadata such as the list of attachments .
2016-05-30 18:29:58 +02:00
* @ param { object } [ options ] - set { useCache : true } to use already fetched data
2014-12-11 17:24:22 +01:00
* @ returns a promise of the HTTP operation
* /
2015-09-08 18:22:37 +02:00
Message . prototype . $reload = function ( options ) {
2016-06-23 22:03:14 +02:00
var _this = this , futureMessageData ;
if ( options && options . useCache && this . $futureMessageData ) {
if ( ! this . isread ) {
Message . $$resource . fetch ( this . $absolutePath ( ) , 'markMessageRead' ) . then ( function ( ) {
Message . $timeout ( function ( ) {
_this . isread = true ;
_this . $mailbox . unseenCount -- ;
} ) ;
} ) ;
}
2016-05-30 18:29:58 +02:00
return this ;
2016-06-23 22:03:14 +02:00
}
2016-05-30 18:29:58 +02:00
2015-09-08 18:22:37 +02:00
futureMessageData = Message . $$resource . fetch ( this . $absolutePath ( options ) , 'view' ) ;
2014-11-25 22:09:55 +01:00
return this . $unwrap ( futureMessageData ) ;
} ;
2017-08-29 17:45:24 +02:00
/ * *
* @ function $parseMailto
* @ memberof Message . prototype
* @ desc Extend the editable content of the message with the
* information parsed from the specified "mailto:" link .
* /
Message . prototype . $parseMailto = function ( mailto ) {
var to , data , match = /^mailto:([^\?]+)/ . exec ( mailto ) ;
if ( match ) {
// Recipients
to = _ . map ( decodeURIComponent ( match [ 1 ] ) . split ( ',' ) , function ( email ) {
return '<' + email . trim ( ) + '>' ;
} ) ;
data = { to : to } ;
// Subject & body
_ . forEach ( [ 'subject' , 'body' ] , function ( param ) {
var re = new RegExp ( param + '=([^&]+)' ) ;
param = ( param == 'body' ) ? 'text' : param ;
match = re . exec ( mailto ) ;
if ( match )
data [ param ] = decodeURIComponent ( match [ 1 ] ) ;
} ) ;
// Other Recipients
_ . forEach ( [ 'cc' , 'bcc' ] , function ( param ) {
var re = new RegExp ( param + '=([^&]+)' ) ;
match = re . exec ( mailto ) ;
if ( match )
data [ param ] = _ . map ( decodeURIComponent ( match [ 1 ] ) . split ( ',' ) , function ( email ) {
return '<' + email . trim ( ) + '>' ;
} ) ;
} ) ;
angular . extend ( this . editable , data ) ;
}
} ;
2015-01-06 04:34:12 +01:00
/ * *
* @ function $reply
* @ memberof Message . prototype
2015-01-06 18:09:14 +01:00
* @ desc Prepare a new Message object as a reply to the sender .
2015-01-06 04:34:12 +01:00
* @ returns a promise of the HTTP operations
* /
Message . prototype . $reply = function ( ) {
2015-01-06 18:09:14 +01:00
return this . $newDraft ( 'reply' ) ;
} ;
/ * *
* @ function $replyAll
* @ memberof Message . prototype
* @ desc Prepare a new Message object as a reply to the sender and all recipients .
* @ returns a promise of the HTTP operations
* /
Message . prototype . $replyAll = function ( ) {
return this . $newDraft ( 'replyall' ) ;
} ;
/ * *
* @ function $forward
* @ memberof Message . prototype
* @ desc Prepare a new Message object as a forward .
* @ returns a promise of the HTTP operations
* /
Message . prototype . $forward = function ( ) {
return this . $newDraft ( 'forward' ) ;
} ;
/ * *
* @ function $newDraft
* @ memberof Message . prototype
* @ desc Prepare a new Message object as a reply or a forward of the current message and associated
* to the draft mailbox .
* @ see { @ link Account . $newMessage }
* @ see { @ link Message . $editableContent }
* @ see { @ link Message . $reply }
* @ see { @ link Message . $replyAll }
* @ see { @ link Message . $forwad }
2015-03-18 18:41:14 +01:00
* @ param { string } action - the HTTP action to perform on the message
2015-01-06 18:09:14 +01:00
* @ returns a promise of the HTTP operations
* /
Message . prototype . $newDraft = function ( action ) {
2015-07-13 21:22:58 +02:00
var _this = this ;
2015-01-06 04:34:12 +01:00
// Query server for draft folder and draft UID
2015-08-26 17:56:47 +02:00
return Message . $$resource . fetch ( this . $absolutePath ( ) , action ) . then ( function ( data ) {
2015-07-13 21:22:58 +02:00
var mailbox , message ;
2015-01-06 18:09:14 +01:00
Message . $log . debug ( 'New ' + action + ': ' + JSON . stringify ( data , undefined , 2 ) ) ;
2015-01-06 04:34:12 +01:00
mailbox = _this . $mailbox . $account . $getMailboxByPath ( data . mailboxPath ) ;
message = new Message ( data . accountId , mailbox , data ) ;
// Fetch draft initial data
2015-07-13 21:22:58 +02:00
return Message . $$resource . fetch ( message . $absolutePath ( { asDraft : true } ) , 'edit' ) . then ( function ( data ) {
2015-08-28 21:50:46 +02:00
Message . $log . debug ( 'New ' + action + ': ' + JSON . stringify ( data , undefined , 2 ) + ' original UID: ' + _this . uid ) ;
2018-01-24 16:32:36 +01:00
var accountDefaults = Message . $Preferences . defaults . AuxiliaryMailAccounts [ _this . $mailbox . $account . id ] ;
if ( accountDefaults . security ) {
if ( accountDefaults . security . alwaysSign )
data . sign = true ;
if ( accountDefaults . security . alwaysEncrypt )
data . encrypt = true ;
}
2015-04-18 14:50:06 +02:00
angular . extend ( message . editable , data ) ;
2015-08-28 21:50:46 +02:00
// We keep a reference to our original message in order to update the flags
message . origin = { message : _this , action : action } ;
2015-07-13 21:22:58 +02:00
return message ;
2015-01-06 04:34:12 +01:00
} ) ;
} ) ;
} ;
2014-12-12 03:52:46 +01:00
/ * *
* @ function $save
* @ memberof Message . prototype
* @ desc Save the message to the server .
* @ returns a promise of the HTTP operation
* /
2014-12-11 17:24:22 +01:00
Message . prototype . $save = function ( ) {
2014-12-16 21:47:34 +01:00
var _this = this ,
2019-11-22 03:51:27 +01:00
data = this . $omit ( ) ;
2014-12-12 03:52:46 +01:00
Message . $log . debug ( 'save = ' + JSON . stringify ( data , undefined , 2 ) ) ;
2014-12-11 17:24:22 +01:00
2014-12-16 21:47:34 +01:00
return Message . $$resource . save ( this . $absolutePath ( { asDraft : true } ) , data ) . then ( function ( response ) {
Message . $log . debug ( 'save = ' + JSON . stringify ( response , undefined , 2 ) ) ;
_this . $setUID ( response . uid ) ;
2016-05-10 20:41:24 +02:00
_this . $reload ( ) ; // fetch a new viewable version of the message
2016-01-25 20:21:38 +01:00
_this . isNew = false ;
2014-12-16 21:47:34 +01:00
} ) ;
2014-12-11 17:24:22 +01:00
} ;
2014-12-12 03:52:46 +01:00
/ * *
* @ function $send
* @ memberof Message . prototype
* @ desc Send the message .
* @ returns a promise of the HTTP operation
* /
2014-12-11 17:24:22 +01:00
Message . prototype . $send = function ( ) {
2015-08-28 21:50:46 +02:00
var _this = this ,
2019-11-22 03:51:27 +01:00
data = this . $omit ( ) ;
2014-12-11 17:24:22 +01:00
2014-12-12 03:52:46 +01:00
Message . $log . debug ( 'send = ' + JSON . stringify ( data , undefined , 2 ) ) ;
2016-07-15 20:03:16 +02:00
return Message . $$resource . post ( this . $absolutePath ( { asDraft : true } ) , 'send' , data ) . then ( function ( response ) {
if ( response . status == 'success' ) {
2015-08-28 21:50:46 +02:00
if ( angular . isDefined ( _this . origin ) ) {
if ( _this . origin . action . startsWith ( 'reply' ) )
_this . origin . message . isanswered = true ;
else if ( _this . origin . action == 'forward' )
_this . origin . message . isforwarded = true ;
}
2016-07-15 20:03:16 +02:00
return response ;
2014-12-11 17:24:22 +01:00
}
else {
2016-07-15 20:03:16 +02:00
return Message . $q . reject ( response . data ) ;
2014-12-11 17:24:22 +01:00
}
} ) ;
} ;
2014-11-25 22:09:55 +01:00
/ * *
* @ function $unwrap
* @ memberof Message . prototype
2016-12-07 20:37:59 +01:00
* @ desc Unwrap a promise .
2014-11-25 22:09:55 +01:00
* @ param { promise } futureMessageData - a promise of some of the Message ' s data
* /
Message . prototype . $unwrap = function ( futureMessageData ) {
2015-08-28 21:15:40 +02:00
var _this = this ;
2014-11-25 22:09:55 +01:00
2016-05-30 18:29:58 +02:00
// Message is not loaded yet
this . $loaded = Message . STATUS . DELAYED _LOADING ;
Message . $timeout ( function ( ) {
if ( _this . $loaded != Message . STATUS . LOADED )
_this . $loaded = Message . STATUS . LOADING ;
} , Message . STATUS . DELAYED _MS ) ;
2015-08-28 21:15:40 +02:00
// Resolve and expose the promise
this . $futureMessageData = futureMessageData . then ( function ( data ) {
2014-11-25 22:09:55 +01:00
// Calling $timeout will force Angular to refresh the view
2015-08-28 21:15:40 +02:00
if ( _this . isread === 0 ) {
2016-09-22 21:51:56 +02:00
_this . isread = true ;
_this . $mailbox . unseenCount -- ;
2014-12-17 20:08:43 +01:00
}
2015-08-28 21:15:40 +02:00
return Message . $timeout ( function ( ) {
2018-03-06 21:22:02 +01:00
delete _this . $parts ;
2016-05-30 18:29:58 +02:00
_this . $loaded = Message . STATUS . LOADED ;
2018-05-15 05:18:30 +02:00
_this . init ( data ) ;
2015-08-28 21:15:40 +02:00
return _this ;
} ) ;
2014-11-25 22:09:55 +01:00
} ) ;
2015-08-28 21:15:40 +02:00
return this . $futureMessageData ;
2014-11-25 22:09:55 +01:00
} ;
2014-12-11 17:24:22 +01:00
/ * *
* @ function $omit
* @ memberof Message . prototype
* @ desc Return a sanitized object used to send to the server .
* @ return an object literal copy of the Message instance
* /
2016-03-15 14:58:58 +01:00
Message . prototype . $omit = function ( options ) {
var message = { } ,
2019-12-09 17:36:39 +01:00
privateAttributes = options && options . privateAttributes ,
source = privateAttributes ? this : this . editable ;
angular . forEach ( source , function ( value , key ) {
if ( _ . includes ( [ 'to' , 'cc' , 'bcc' ] , key ) && ! privateAttributes ) {
2019-11-22 03:51:27 +01:00
message [ key ] = _ . map ( value , function ( addr ) {
return addr . toString ( ) ;
} ) ;
}
else if ( key != 'constructor' && key [ 0 ] != '$' || privateAttributes ) {
2014-12-11 17:24:22 +01:00
message [ key ] = value ;
}
} ) ;
return message ;
} ;
2016-05-04 17:12:30 +02:00
/ * *
2016-12-07 20:37:59 +01:00
* @ function download
2016-05-04 17:12:30 +02:00
* @ memberof Message . prototype
* @ desc Download the current message
* @ returns a promise of the HTTP operation
* /
2016-12-07 20:37:59 +01:00
Message . prototype . download = function ( ) {
var data , options ;
2016-05-04 17:12:30 +02:00
2016-12-07 20:37:59 +01:00
data = { uids : [ this . uid ] } ;
options = { filename : this . subject + '.zip' } ;
2016-05-04 17:12:30 +02:00
2016-12-07 20:37:59 +01:00
return Message . $$resource . download ( this . $mailbox . id , 'saveMessages' , data , options ) ;
} ;
/ * *
* @ function downloadAttachments
* @ memberof Message . prototype
* @ desc Download an archive of all attachments
* @ returns a promise of the HTTP operation
* /
Message . prototype . downloadAttachments = function ( ) {
var options ;
options = { filename : l ( 'attachments' ) + "-" + this . uid + ".zip" } ;
return Message . $$resource . download ( this . $absolutePath ( ) , 'archiveAttachments' , null , options ) ;
2016-05-04 17:12:30 +02:00
} ;
2014-11-25 22:09:55 +01:00
} ) ( ) ;